Chapter 06. 장고의 Queryset 2편 (Retrieve 심화)
지난 편에 이어서 QuerySet의 조회에 대한 상세한 내용을 정리했다.
데이터 객체 조회하기 ⅱ(Retrieving objects)
단일한 데이터 객체 조회하기(Retrieving a single object with get())
- get() : 단 하나의 데이터 객체만을 조회한다
filter()는 결과가 0개, 1개 혹은 여러개의 QuerySet을 불러올 때 사용할 수 있는 데 반해, get()은 단 하나의 결과 만을 얻어야 할 때 사용한다. get()은 결과가 0개라면 DoesNotExist(결과가 존재하지 않는다)를 발생(raise)시키고, 여러개라면 MultipleObjectsReturned(여러개의 결과가 리턴됐다)를 발생(raise)시킨다. 따라서 get()은 Primary Key처럼 레코드마다 고유한 값을 가지고 있는 속성을 조회할 때 사용할 수 있다.
데이터 객체 수 제한하기(Limiting QuerySets)
QuerySet의 개수를 제한하는 방법은 파이썬의 Slicing을 이용하면 된다.
아래의 예제들로 slicing을 사용한 각 케이스들의 결과를 보자
from articles.models import Article
article = Article.objects.filter(user_id=1)
# 1
article
# 2
article[:5]
# 3
article[-1]
# 4
article[::2]
# 5
article[::2].filter(title__contains="아")
# 1
<QuerySet [<Article: 야>,<Article: 아>, ... , <Article: 나폴리탄>]>
# 2
<QuerySet [<Article: 야>, <Article: 아>, <Article: 오예>, <Article: 오예>, <Article: 아이>]>
# 3
ValueError: Negative indexing is not supported.
# 4
[<Article: 야>, <Article: 오예>, <Article: 아이>, <Article: 수제비>, <Article: 새벽코딩>]
# 5
AttributeError: 'list' object has no attribute 'filter'
# 1
Article모델에서 user_id가 1인 QuerySet객체가 잘 출력되었다.
# 2
1번의 결과에서 slicing을 통해 0~4번 QuerySet객체가 잘 출력되었다
# 3
QuerySet을 slicing할 때 음수를 이용한 인덱싱은 지원하지 않는다
# 4
2step씩 건너뛰며 slicing을 한 경우에도 QuerySet은 잘 출력된다. 하지만 결과가 QuerySet객체가 아닌 파이썬 리스트 형태로 출력됐다는 점에 주목하자
# 5
4번의 결과에 다시 한번 filter()로 조회를 했을 때 AttributeError가 발생했다. QuerySet을 slicing할 때 step을 사용했다면 그 결과에 다시 필터링을 하거나 정렬하는 것은 금지된다.
필드 조회하기(Field lookups)
Field lookups은 SQL의 WHERE절과 같이 어떤 Field의 필터링을 할 때 사용한다. 이는 QuerySet메소드인 filter(), exclude(), get()의 키워드 인수(Keyword Arguments)에 적용해 사용할 수 있다. 형태는 field__lookuptype=value와 같이 던더스코어로 연결된 형태이다. 아래는 몇가지 lookuptype에 대한 예제들이다.(field lookups는 2023년 4월 24일 기준 24가지가 있다)
- exact : 정확히 일치한다. lookup의 기본값
Article.objects.filter(title__exact="아")
Article.objects.filter(title="아")
둘의 결과는 완전히 같다. 이는 exact한 조회가 일반적인 형태이기 때문에 lookuptype이 없어도 장고가 알아서 exact한 값을 찾는 것으로 가정하기 때문이다.
- iexact : 대소문자 구별하지 않고 정확히 일치한다.
article1 = Article.objects.create(user_id=1, title="Abcd", content="a")
article2 = Article.objects.create(user_id=1, title="AbCD", content="a")
article1.save()
article2.save()
Article.objects.filter(title__iexact="abcd")
<QuerySet [<Article: Abcd>, <Article: AbCD>]>
- contains : 대소문자 관계없이 어떤 문자열을 포함한다.
article1 = Article.objects.create(user_id=1, title="Abcd", content="a")
article2 = Article.objects.create(user_id=1, title="AbCD", content="a")
article1.save()
article2.save()
Article.objects.filter(title__contains="Abc")
<QuerySet [<Article: Abcd>, <Article: AbCD>]>
- in : iterable한 값 list, tuple, queryset을 포함한다. 자주 쓰진 않지만 문자열도 가능하다. SQL의 IN과 같다
from articles.models import Article
from users.models import User
# 1
Article.objects.filter(user_id__in=[2,3])
# 2
inner_qs = User.objects.filter(age__gte=20)
Article.objects.filter(user_id__in=inner_qs)
# 1
<QuerySet [<Article: 안녕하세요>, <Article: 안녕하세요 Ganet입니다>, <Article: 김치말이>, <Article: 오늘도>]>
# 2
<QuerySet [<Article: 야>, <Article: 아>, <Article: 오예>, <Article: 오예>, <Article: 아이>, <Article: 감자>, <Article: 수제비>, <Article: 마이클>, <Article: 새벽코딩>, <Article: 나폴리 탄>, <Article: Abcd>, <Article: AbCd>, <Article: Abcd>, <Article: Abcd>, <Article: AbCD>, <Article: 안녕하세요>, <Article: 안녕하세요 Ganet입니다>]>
# 1
위와 같이 list를 값으로 넣어 user_id중에 2, 3을 포함하는 레코드를 조회할 수 있다
# 2
inner_qs에 user테이블에서 age가 20 이상인 레코드를 저장하고 이걸 다시 article 테이블의 user_id를 포함하는 값만 추려서 출력했다. 이를 SQL문으로 보면 다음과 같다.
[
{'sql': 'SELECT "articles_article"."id", "articles_article"."user_id", "articles_article"."title", "articles_article"."content", "articles_article"."created_at", "articles_article"."updated_at" FROM "articles_article" WHERE "articles_article"."user_id" IN (SELECT U0."superduper_id" FROM "user" U0 WHERE U0."age" >= 20) LIMIT 21', 'time': '0.000'}
]
inner_qs에 values("필드명")을 한번 더 걸어서 비교할 필드를 user_id가 아닌 다른 필드로 바꿔줄 수도 있다
이외에도 대소를 비교하는 gt, gte, lt, lte, 시간 관련한 week, date, year, month, day, time, hour, minute, second,심지어 정규표현식으로 검색할 수 있는 regex까지 지원한다.
'프로그래밍 > Python' 카테고리의 다른 글
[Python] 의존성 관리툴 poetry (2) | 2023.05.30 |
---|---|
[DRF] dj-rest-auth - email인증 및 User모델 필드 커스텀하기 (2) | 2023.05.16 |
[Django] 장고 기초 - (4) Query Set(1) (2) | 2023.04.22 |
[Django] 장고 기초 - (3) Models (0) | 2023.04.20 |
[Django] 장고 기초 - (2) Template Language (0) | 2023.04.17 |