Django queryset - filter query, query condition, field lookups

정현우·2022년 10월 14일
3

Django Basic to Advanced

목록 보기
7/38

Django queryset - filter

Queryset 에 objectjs 로 접근해 "filter", "get" 등의로 질의의 조건을 만들 수 있다. 다음 시리즈 Query Expressions 1, Query Expressions 2 게시물에서 조금 더 깊은 내용을 같이 고찰할 수 있다.

  • queryset은 django ORM에서 사용하는 자료구조 형으로 DB에서 전달 받은 결과값들을 ORM에 맞는 object(Model)의 목록 또는 단일 값으로 존재한다.

filter

  • official docs에서는 "Returns a new QuerySet containing objects that match the given lookup parameters." 와 같이 filter 함수를 정의한다.

  • 사실 지금 다뤄 볼 것은 filter 에만 국한되는 것이 아니라 정확하겐 Django ORM에서 query에 대한 condition을 할 수 있는 다양한 것들을 살펴보려고 한다. Django에서는 이것들을 Field lookups 이라고 부른다.

  • 아래는 실습에 사용할 model 이다.
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() # 음수값 불가능

query condition

1. exact, iexact

  • 정확하게 일치하는 데이터 찾기 (equal condition)
>>> 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)...']>

2. contains, icontains

  • 특정 문자열을 포함하는 데이터 찾기 (like condition)
>>> 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)...']>

3. in

  • list, tuple, string 또는 queryset과 같이 iterable한 객체를 대상으로 각 원소 조회 및 데이터 찾기
>>> 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)>]>

4. gt, gte, lt, lte

  • 대소 비교 조건들이다.
  • gt(Greater than > ), gte(Greater than or equal to >=)
  • lt(Less than < ), lte(Less than or equal to <=)
>>> 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)>]>

5. startswith, istartswith, endswith, iendswith

  • 조건 다음 문자열로 시작하는 것, 끝나는 것, (i 붙으면) 대소 비교 없이 찾는 것이다.
  • contains에서 와일드카드의 위치가 바뀌는 것이라고 생각하면 된다.
>>> 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

6. range

  • 해당 범위 내에 포함되는 데이터들을 찾는것이다.
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';

7. DB단에서 제공하는 다양한 날짜 관련 함수 및 연산

  • date, year, iso_year, month .... 등 활용도 높은 날짜 관련 함수들이 많다. 시간과 관련된 것도 많다. (DateTimeField 대상으로)

  • 공식 홈페이지 가이드 에서 꼭 살펴보는 것을 추천한다.

9. isnull

  • 말 그대로 특정 field가 null 인경우 True로 체크해서 찾는 데이터로 세팅이 된다.
Entry.objects.filter(pub_date__isnull=True)

# SQL
SELECT ... WHERE pub_date IS NULL;

10. regex, iregex

  • 정규식도 활용 할 수 있다. 물론 i 가 붙으면 대소 비교는 안한다. DB단에서의 regex를 하기 때문에 퍼포먼스 체크를 할 때 trade off 에 대한 고려가 필요하다.
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

Query 최적화, django-debug-toolbar

  • 사실 raw query를 많이 비추천하고, ORM을 사용하라고 공식 가이드에서 강조한다. 코어 부분을 보면 raw query가 가지는 보안적인 부분과 다시 객체를 바인딩하는 부분에서 오버헤드가 있는 것을 알 수 있다.

  • 하지만 ORM으로 최적화를 한다는 것이 쉽지가 않다. 그래서 ORM -> SQL query -> 튜닝 -> 다시 ORM 의 방향을 많이 가는 것 같다.

  • 그때 https://django-debug-toolbar.readthedocs.io/en/latest/ 를 활용하면 좋다.

  • 공식 깃허브 에서 쉽게 사용법을 확인할 수 있다.


출처

profile
도메인 중심의 개발, 깊이의 가치를 이해하고 “문제 해결” 에 몰두하는 개발자가 되고싶습니다. 그러기 위해 항상 새로운 것에 도전하고 노력하는 개발자가 되고 싶습니다!

0개의 댓글