django QuerySet 기본

dingdong·2022년 1월 12일
0

BASIC ORM

목록 보기
2/2
post-custom-banner

Model Manager

데이터베이스의 질의 인터페이스를 제공
디폴트 Manager 로서 ModelCls.objects 가 제공

# SELCT * FROM app_model;
ModelsCls.objects.all()


# SELCT * FROM app_model ORDER BY ID DESC LIMIT 10;
ModelsCls.objects.all().order_by('-id')[:10]


# INSERT INTO app_model (title) VALUES ("New Title");

ModelsCls.objects.create(title="New Title")

>>> qs = Post.objects.all()

>>> print(qs)
<QuerySet [<Post: 첫번째 메세지>, <Post: 두번째 메세지>, <Post: 세번째 메세지>]>
>>>

>>> print(type(qs))
<class 'django.db.models.query.QuerySet'>
>>> 

print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post"
>>>

오름차순

  • 단, 여러개의 필드를 정렬할 수 있지만
    1~2개 정도만 정렬을하고
    그 이상은 성능저하를 야기 시킬수 있기 때문에
    사용하지 않는다.
    ex) order_by('id', 'is_pulic')
>>> qs = Post.objects.all().order_by('id')


>>> print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post" ORDER BY "instagram_post"."id" ASC
>>>

>>> qs
<QuerySet [<Post: 첫번째 메세지>, <Post: 두번째 메세지>, <Post: 세번째 메세지>]>
>>>

## id 에 대해서 내림차순
>>> qs = Post.objects.all().order_by('-id')

>>> print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post" ORDER BY "instagram_post"."id" DESC


>>> qs
<QuerySet [<Post: 세번째 메세지>, <Post: 두번째 메세지>, <Post: 첫번째 메세지>]>
>>>

내림차순

  • 단, 여러개의 필드를 정렬할 수 있지만
    1~2개 정도만 정렬을하고
    그 이상은 성능저하를 야기 시킬수 있기 때문에
    사용하지 않는다.
    ex) order_by('-id', '-is_pulic')
>>> qs = Post.objects.all().order_by('-id')[:2]


>>> print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post" ORDER BY "instagram_post"."id" DESC LIMIT 2


>>> qs
<QuerySet [<Post: 세번째 메세지>, <Post: 두번째 메세지>]>
>>>

순회 가능 (for, loop 가능하다)

>>> for post in qs:
...     print(post.id, post.message, post.created_at)
...     print("id: {id}, message: {message} {created_at}".format(**post.__dict__))


...
4 세번째 메세지 2022-01-12 11:07:37.336965+00:00
id: 4, message: 세번째 메세지 2022-01-12 11:07:37.336965+00:00
2 두번째 메세지 2022-01-11 10:56:29.125509+00:00
id: 2, message: 두번째 메세지 2022-01-11 10:56:29.125509+00:00
>>>

QuerySet은 Chaining 을 지원

  • 속성에 따라서 filter 를 사용하는 것이 다름.
    • (ex) message__startswith='첫번째'
      message 필드에서 첫번째로 시작하는 것을 필터링
  • TIP. 한줄이 길어지면 \ 사용
>>> qs = Post.objects.all().filter(message__startswith='첫번째')


>>> print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post" WHERE "instagram_post"."message" LIKE 첫번째% ESCAPE '\'


>>> qs
<QuerySet [<Post: 첫번째 메세지>]>
>>>


>>> qs = Post.objects.all().filter(message__icontains='첫번째').order_by('-id')
>>> print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post" WHERE "instagram_post"."message" LIKE %첫번째% ESCAPE '\' ORDER BY "instagram_post"."id" DESC
>>> qs
<QuerySet [<Post: 첫번째 메세지>]>
>>>


>>> qs = Post.objects.all()\
...      .filter(message__icontains='첫번째')\
...      .order_by('-id')
>>>
>>> qs
<QuerySet [<Post: 첫번째 메세지>]>
>>>

QuerySet

  • SQL 을 생성해주는 인터페이스
  • 순회가능한 객체 (for, loop 에서 사용 가능)
  • Model Manager 를 통해, 해당 Model 에 대한 QuerySet 을 획득
    • Post.objects.all() 코드는 "SELECT * FROM post ...;"
    • Post.objects.create(...) 코드는 "INSERT INTO ...;"
  • QuerySet은 Chaining 을 지원
    • Post.objects.all().filter(...).exclude(...).filter(...)
  • QuerySet은 Lazy한 특성
    • QuerySet을 만드는 동안에는 DB접근을 하지 않는다.
    • 실제로 데이터가 필요한 시점에 접근을 합니다.
      • 데이터가 필요한 시점은 언제?
        1) queryset
        • repr : 파이썬 인터프리터를 위한 것, API를 인터프리터로 사용할 때 즉시 결과를 볼 수 있다.
        2) print(queryset)
        3) list(queryset)
        4) for instance in queryset: print(instance)

다양한 조회 요청 방법 (SECLT SQL 생성)

조건을 추가한 Queryset, 획득할 준비

  • queryset.filter(...) -> queryset
  • queryset.exclude(...) -> queryset
 # filter  : WHERE 문
 >>> qs = Post.objects.all().filter(message__icontains='첫번째').order_by('-id')
 
>>> print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post" WHERE "instagram_post"."message" LIKE %첫번째% ESCAPE '\' ORDER BY "instagram_post"."id" DESC
>>>
 # exclude  : WHERE NOT 문
 
>>> qs = Post.objects.all().exclude(message__icontains='첫번째').order_by('-id')
>>> print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post" WHERE NOT ("instagram_post"."message" LIKE %첫번째% ESCAPE '\') ORDER BY "instagram_post"."id" DESC
>>> qs
<QuerySet [<Post: 세번째 메세지>, <Post: 두번째 메세지>]>
>>>

특정 모델객체 1개 획득을 시도

  • queryset[숫자인덱스]
    • 모델 객체 혹은 예외발생 (IndexError)
>>> qs = Post.objects.all()
>>> qs[1]
<Post: 두번째 메세지>
>>> qs[0]
<Post: 첫번째 메세지>
>>>
>>> qs[3]
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/root/django-with-react-study/venv/lib/python3.8/site-packages/django/db/models/query.py", line 318, in __getitem__
    return qs._result_cache[0]
IndexError: list index out of range
  • queryset.get(...)
    • 모델 객체 혹은 예외 발생 (DoseNotExist, MultipleObjectsReturned)
>>> qs = Post.objects.all()
>>> qs.get(pk=1)
<Post: 첫번째 메세지>
>>> qs.get(pk=2)
<Post: 두번째 메세지>
>>>
>>> qs.get(pk=3)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/root/django-with-react-study/venv/lib/python3.8/site-packages/django/db/models/query.py", line 435, in get
    raise self.model.DoesNotExist(
instagram.models.Post.DoesNotExist: Post matching query does not exist.
>>> 

# lte: less than equal
>>> qs.get(id__lte=2)   
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/root/django-with-react-study/venv/lib/python3.8/site-packages/django/db/models/query.py", line 439, in get
    raise self.model.MultipleObjectsReturned(
instagram.models.Post.MultipleObjectsReturned: get() returned more than one Post -- it returned 2!
>>>
  • queryset.first()
    • 모델 객체 혹은 None
>>> qs = Post.objects.all()
>>> qs.first()
<Post: 첫번째 메세지>
>>>

>>> qs.none()
<QuerySet []>
>>> qs.none().first()
>>>
  • queryset.last()
    • 모델 객체 혹은 None
>>> qs = Post.objects.all()
>>> qs.last()
<Post: 세번째 메세지>
>>>

filter < - > exclude

  • filter 와 exclude는 서로 반대되는 개념
  • SELECT 쿼리에 WHERE 조건 추가
  • 인자로 '필드명 = 조건값' 지정
  • 1개 이상의 인자 지정 -> 모두 AND 조건으로 묶임.
 >>> qs = Post.objects.all()
 >>> qs = qs.exclude(id__gte=2, message__icontains="첫번째")
 
>>> print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post" WHERE NOT ("instagram_post"."id" >= 2 AND "instagram_post"."message" LIKE %첫번째% ESCAPE '\')

>>> qs
<QuerySet [<Post: 첫번째 메세지>, <Post: 두번째 메세지>, <Post: 세번째 메세지>]>
>>>
>>>


>>> qs = Post.objects.all()

>>> qs = qs.filter(id__gte=2, message__icontains="첫번째")

>>> print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post" WHERE ("instagram_post"."id" >= 2 AND "instagram_post"."message" LIKE %첫번째% ESCAPE '\')
>>> qs
<QuerySet []>
>>>
  • OR 조건을 묶으려면, django.db.models.Q 활용
 >>> from django.db.models import Q


>>> qs = Post.objects.all()
>>> qs = qs.filter(Q(id__gte=2) | Q(message__icontains="첫번째"))

>>> qs.query
<django.db.models.sql.query.Query object at 0x7f68dbe7ab80>

>>> print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post" WHERE ("instagram_post"."id" >= 2 OR "instagram_post"."message" LIKE %첫번째% ESCAPE '\')

>>> qs
<QuerySet [<Post: 첫번째 메세지>, <Post: 두번째 메세지>, <Post: 세번째 메세지>]>
>>>


>>> qs = Post.objects.all()

>>> cond = Q(id__gte=2) | Q(message__icontains='첫번째')

>>> qs = qs.filter(cond)

>>> print(qs.query)
SELECT "instagram_post"."id", "instagram_post"."message", "instagram_post"."photo", "instagram_post"."is_public", "instagram_post"."created_at", "instagram_post"."updated_at" FROM "instagram_post" WHERE ("instagram_post"."id" >= 2 OR "instagram_post"."message" LIKE %첫번째% ESCAPE '\')

>>> qs
<QuerySet [<Post: 첫번째 메세지>, <Post: 두번째 메세지>, <Post: 세번째 메세지>]>
>>>

필드 타입별 다양한 조건 매칭

주의) 데이터베이스 타입에 따라서 생성되는 SQL이 다릅니다.

숫자/날짜/시간 필드의 경우

1) 필드명__lt = 조건값

  • less than
  • 필드명 < 조건값
    2) 필드명__lte = 조건값
  • less than equal
  • 필드명 <= 조건값
    3) 필드명__gt = 조건값
  • greater than
  • 필드명 > 조건값
    4) 필드명__gte = 조건값
  • greater than equal
  • 필드명 >= 조건값

문자열 필드의 경우

1) 필드명__startswith = 조건값

  • 필드명 LIKE "조건값%"
    2) 필드명__istartswith = 조건값
  • 필드명 ILIKE "조건값%"
  • I는 ignore case : 대소문자 구별 안함
    3) 필드명__endswith = 조건값
  • 필드명 LIKE "%조건값"
    4) 필드명__iendswith = 조건값
  • 필드명 ILIKE "%조건값"
  • I는 ignore case : 대소문자 구별 안함
    5) 필드명__contains = 조건값
  • 필드명 LIKE "%조건값%"
    6) 필드명__icontains = 조건값
  • 필드명 ILIKE "%조건값%"
  • I는 ignore case : 대소문자 구별 안함

정렬 조건 추가

SELECT 쿼리에 ORDER BY 추가

정렬 조건을 추가하지 않으면 일관된 순서를 보장받을 수 없음

DB에서 다수의 필드에 대한 정렬을 지원

하지만 가급적 단일 필드로 하는 것이 성능에 이익
시간숙/역순 정렬이 필요할 경우, ID 필드를 활용

정렬 조건을 지정하는 2가지 방법
  1. 모델 클래스의 Meta 속성으로 ordering 설정: list 로 지정
class Post(models.Model):

    class Meta:
        ordering = ['-id']
  1. 모든 Queryset에 order_by(...)에 지정

    • queryset에서 order_by 를 지정했다면,
      모델 클래스의 Meta 속성을 통해서 기본 정렬한게 무시됨.
>>> Post.objects.all().order_by('created_at')
SELECT "instagram_post"."id",
       "instagram_post"."message",
       "instagram_post"."photo",
       "instagram_post"."is_public",
       "instagram_post"."created_at",
       "instagram_post"."updated_at"
  FROM "instagram_post"
 ORDER BY "instagram_post"."created_at" ASC
 LIMIT 21
Execution time: 0.000249s [Database: default]
<QuerySet [<Post: 첫번째 메세지>, <Post: 두번째 메세지>, <Post: 세번째 메세지>]>
>>>

QuerySet에 범위 조건 추가

슬라이싱을 통한 범위조건 추가

  • SELECT 쿼리에 OFFSET/LIMIT
  • 슬라이싱 지원됨. 단, 역순슬라이싱은 지원 안됨
    • 왜 지원이 안되는가? 데이터베이스에서 지원하지 않기 때문에
 객체[start:stop:step]

  - offset : start
  - LIMIT : stop - start
  - STEP은 Query에 대응되지 않음.
>>> Post.objects.all()[:2]
SELECT "instagram_post"."id",
       "instagram_post"."message",
       "instagram_post"."photo",
       "instagram_post"."is_public",
       "instagram_post"."created_at",
       "instagram_post"."updated_at"
  FROM "instagram_post"
 ORDER BY "instagram_post"."id" DESC
 LIMIT 2
Execution time: 0.000153s [Database: default]
<QuerySet [<Post: 세번째 메세지>, <Post: 두번째 메세지>]>



>>> Post.objects.all()[-2:]
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/root/django-with-react-study/venv/lib/python3.8/site-packages/django/db/models/query.py", line 294, in __getitem__
    assert ((not isinstance(k, slice) and (k >= 0)) or
AssertionError: Negative indexing is not supported.
>>>


>>> Post.objects.all().order_by('id')[:2]
SELECT "instagram_post"."id",
       "instagram_post"."message",
       "instagram_post"."photo",
       "instagram_post"."is_public",
       "instagram_post"."created_at",
       "instagram_post"."updated_at"
  FROM "instagram_post"
 ORDER BY "instagram_post"."id" ASC
 LIMIT 2
Execution time: 0.000208s [Database: default]
<QuerySet [<Post: 첫번째 메세지>, <Post: 두번째 메세지>]>
>>>
  • step 이 들어가면
    STEP 사용하지 않으면 QuerySet 으로 return 됨.
    STEP을 사용하면 List 로 return 이됨.
    • step 사용 X : <QuerySet [<Post: 두번째 메세지>, <Post: 첫번째 메세지>]>
    • step 사용 O: [<Post: 두번째 메세지>, <Post: 첫번째 메세지>]
  • STEP은 SQL에 개념이 없음.
    STEP은 django의 QuerySet이 처리함.
  • 밑에 예제를 기준으로 [1:3] 만큼 SQL로 처리를 한 다음 django가 2(STEP) 을 처리함.
>>> Post.objects.all()[1:3]
SELECT "instagram_post"."id",
       "instagram_post"."message",
       "instagram_post"."photo",
       "instagram_post"."is_public",
       "instagram_post"."created_at",
       "instagram_post"."updated_at"
  FROM "instagram_post"
 ORDER BY "instagram_post"."id" DESC
 LIMIT 2
OFFSET 1
Execution time: 0.000204s [Database: default]
<QuerySet [<Post: 두번째 메세지>, <Post: 첫번째 메세지>]>
>>>


>>> Post.objects.all()[1:3:1]
SELECT "instagram_post"."id",
       "instagram_post"."message",
       "instagram_post"."photo",
       "instagram_post"."is_public",
       "instagram_post"."created_at",
       "instagram_post"."updated_at"
  FROM "instagram_post"
 ORDER BY "instagram_post"."id" DESC
 LIMIT 2
OFFSET 1
Execution time: 0.000120s [Database: default]
[<Post: 두번째 메세지>, <Post: 첫번째 메세지>]
>>>
profile
자동화 개발
post-custom-banner

0개의 댓글