Queryset
에 objectjs 로 접근해 "filter", "get" 등의로 질의의 조건을 만들 수 있다. 다음 시리즈 Query Expressions 1, Query Expressions 2 게시물에서 조금 더 깊은 내용을 같이 고찰할 수 있다.
official docs에서는 "Returns a new QuerySet containing objects that match the given lookup parameters." 와 같이 filter
함수를 정의한다.
사실 지금 다뤄 볼 것은 filter
에만 국한되는 것이 아니라 정확하겐 Django ORM에서 query에 대한 condition을 할 수 있는 다양한 것들을 살펴보려고 한다. Django에서는 이것들을 Field lookups
이라고 부른다.
from django.db import models
class ItemCategory(models.Model):
category_name = models.CharField(max_length=4, blank=False, null=False)
class Items(models.Model):
category = models.ForeignKey(ItemCategory, on_delete=models.CASCADE)
name = models.CharField(max_length=100, blank=False, null=False)
quantity = models.PositiveSmallIntegerField() # 음수값 불가능
>>> Items.objects.filter(name__exact="book")
<QuerySet []>
>>> Items.objects.filter(name__exact="comic book 의류")
<QuerySet [<Items: Items object (5)>]>
>>> res = Items.objects.filter(name__exact="comic book 의류")
>>> res
<QuerySet [<Items: Items object (5)>]>
>>> Items.objects.filter(category__exact=1)
<QuerySet [<Items: Items object (1)>, <Items: Items object (2)>, <Items: Items object (3)>, <Items: Items object (4)>, <Items: Items object (5)>, <Items: Items object (6)>, <Items: Items object (7)>, <Items: Items object (8)>, <Items: Items object (9)>, <Items: Items object (10)>, <Items: Items object (11)>, <Items: Items object (12)>, <Items: Items object (13)>, <Items: Items object (14)>, <Items: Items object (15)>, <Items: Items object (16)>, <Items: Items object (17)>, <Items: Items object (18)>, <Items: Items object (19)>, <Items: Items object (20)>, '...(remaining elements truncated)...']>
>>> Items.objects.filter(name__contains="BOOK")
<QuerySet []>
>>> Items.objects.filter(name__icontains="BOOK")
<QuerySet [<Items: Items object (5)>, <Items: Items object (107)>, <Items: Items object (140)>, <Items: Items object (159)>, <Items: Items object (181)>, <Items: Items object (202)>, <Items: Items object (326)>, <Items: Items object (328)>, <Items: Items object (376)>, <Items: Items object (388)>, <Items: Items object (394)>, <Items: Items object (426)>, <Items: Items object (454)>, <Items: Items object (510)>, <Items: Items object (599)>, <Items: Items object (614)>, <Items: Items object (705)>, <Items: Items object (731)>, <Items: Items object (819)>, <Items: Items object (830)>, '...(remaining elements truncated)...']>
>>> Items.objects.filter(category__in=[1, 2, 3])
<QuerySet [<Items: Items object (1)>, <Items: Items object (2)>, <Items: Items object (3)>, <Items: Items object (4)>, <Items: Items object (5)>, <Items: Items object (6)>, <Items: Items object (7)>, <Items: Items object (8)>, <Items: Items object (9)>, <Items: Items object (10)>, <Items: Items object (11)>, <Items: Items object (12)>, <Items: Items object (13)>, <Items: Items object (14)>, <Items: Items object (15)>, <Items: Items object (16)>, <Items: Items object (17)>, <Items: Items object (18)>, <Items: Items object (19)>, <Items: Items object (20)>, '...(remaining elements truncated)...']>
>>> ItemCategory.objects.filter(id__in=[1, 2, 3])
<QuerySet [<ItemCategory: ItemCategory object (1)>, <ItemCategory: ItemCategory object (2)>, <ItemCategory: ItemCategory object (3)>]>
>>> Items.objects.filter(quantity__gte=100)
<QuerySet [<Items: Items object (69)>, <Items: Items object (111)>, <Items: Items object (171)>, <Items: Items object (313)>, <Items: Items object (444)>, <Items: Items object (507)>, <Items: Items object (535)>, <Items: Items object (568)>, <Items: Items object (816)>, <Items: Items object (850)>, <Items: Items object (933)>]>
>>> Items.objects.filter(name__startswith="C")
<QuerySet [<Items: Items object (50)>, <Items: Items object (128)>, <Items: Items object (149)>, <Items: Items object (355)>, <Items: Items object (591)>]>
>>> len(Items.objects.filter(name__startswith="C"))
5
>>> Items.objects.filter(name__istartswith="C")
<QuerySet [<Items: Items object (5)>, <Items: Items object (8)>, <Items: Items object (11)>, <Items: Items object (15)>, <Items: Items object (32)>, <Items: Items object (36)>, <Items: Items object (37)>, <Items: Items object (50)>, <Items: Items object (52)>, <Items: Items object (60)>, <Items: Items object (70)>, <Items: Items object (71)>, <Items: Items object (73)>, <Items: Items object (77)>, <Items: Items object (82)>, <Items: Items object (91)>, <Items: Items object (116)>, <Items: Items object (127)>, <Items: Items object (128)>, <Items: Items object (135)>, '...(remaining elements truncated)...']>
>>> len(Items.objects.filter(name__istartswith="C"))
129
Items.objects.filter(quantity__range=(1, 10))
datetime
과 함께 사용하여 날짜 사이 값 찾을 때 유용하다.import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))
# SQL은 아래와 같다
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
date, year, iso_year, month .... 등 활용도 높은 날짜 관련 함수들이 많다. 시간과 관련된 것도 많다. (DateTimeField 대상으로)
공식 홈페이지 가이드 에서 꼭 살펴보는 것을 추천한다.
Entry.objects.filter(pub_date__isnull=True)
# SQL
SELECT ... WHERE pub_date IS NULL;
Entry.objects.get(title__regex=r'^(An?|The) +')
# SQL
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL
SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle
SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL
SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite
사실 raw query를 많이 비추천하고, ORM을 사용하라고 공식 가이드에서 강조한다. 코어 부분을 보면 raw query가 가지는 보안적인 부분과 다시 객체를 바인딩하는 부분에서 오버헤드가 있는 것을 알 수 있다.
하지만 ORM으로 최적화를 한다는 것이 쉽지가 않다. 그래서 ORM -> SQL query -> 튜닝 -> 다시 ORM
의 방향을 많이 가는 것 같다.
그때 https://django-debug-toolbar.readthedocs.io/en/latest/ 를 활용하면 좋다.
공식 깃허브 에서 쉽게 사용법을 확인할 수 있다.