데이터베이스 안에 있는 객체 중 해당 QuerySet과 맞는 객체 수(정수)를 반환한다.
>>> from posting.models import *
>>> from user.models import *
>>> Posting.objects.count()
8
>>> Posting.objects.filter(username_id=32).count()
1
** 만일 QuerySet에 있는 아이템의 수 및 model instance가 필요하다면 len(QuerySet)을 쓰는게 훨씬 효과적이다. .count()
는 추가적인 database query를 만들기 때문이다.
해당 메소드는 QuerySet에 결과값이 있으면 True를, 없으면 False를 반환한다.
>>> Posting.objects.filter(username_id=32).exists()
True
해당 field에 저장되어있는 데이터를 다른 값으로 업데이트할 수 있다. 해당 Query와 맞는 수(정수)를 반환한다.
save()
를 해서 데이터베이스에 해당 변경값을 저장해야 한다. >>> Posting.objects.get(id=9)
<Posting: Posting object (9)>
>>> q = Posting.objects.get(id=9)
>>> q.description = '바뀔 예정'
>>> q.save()
save()
할 필요 없이 바로 데이터베이스에 업데이트 된다. >>> Posting.objects.filter(id=9).update(description='한번 더 바뀔 예정')
1
해당 row에 저장되어 있는 데이터를 삭제한다. 해당 Query와 맞는 수(정수),객체 타입과 해당되는 객체 수를 포함한 딕셔너리를 반환한다.
>>> Posting.objects.filter(description='한번 더 바뀔 예정').delete()
(1, {'posting.Posting': 1})
이 때 FK값이 만약 ON DELETE CASCADE으로 저장되어 있으면 삭제할 객체를 가리키는 FK가 있는 객체 또한 함께 삭제된다.
해당 QuerySet과 맞는 첫번째 객체 반환하며, 만약 맞는 객체가 없을 시 None
을 반환한다. 만일 명시된 .order_by()
가 없을 경우 자동으로 PK값으로 나열한다.
>>> Posting.objects.order_by('description').first()
<Posting: Posting object (2)>
>>> Posting.objects.order_by('description')[0]
<Posting: Posting object (2)>
order_by('description').first()
과 order_by('description')[0]
둘 다 똑같은 값을 반환한다.
해당 QuerySet과 맞는 마지막 객체 반환하며, 만약 맞는 객체가 없을 시 None
을 반환한다. .first()
메소드처럼 만일 명시된 .order_by()
가 없을 경우 자동으로 PK값으로 나열한다.
>>> Posting.objects.order_by('description').last()
<Posting: Posting object (7)>
>>> Posting.objects.order_by('description')[-1]
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/mjhuh/miniconda3/envs/westagram/lib/python3.9/site-packages/django/db/models/query.py", line 301, in __getitem__
assert ((not isinstance(k, slice) and (k >= 0)) or
AssertionError: Negative indexing is not supported.
>>> Posting.objects.order_by('description')[6]
<Posting: Posting object (7)>
Negative indexing
은 AssertionError
를 띄운다.
>>> Posting.objects.filter(username_id=1).order_by('?')
<QuerySet [<Posting: Posting object (2)>, <Posting: Posting object (3)>, <Posting: Posting object (8)>]>
>>> Posting.objects.filter(username_id=1).order_by('-image_url')
<QuerySet [<Posting: Posting object (3)>, <Posting: Posting object (2)>, <Posting: Posting object (8)>]>
QuerySet에서 반환되는 결과는 model의 Meta에서 ordering 옵션에 의해 제공되는 order tuple에 의해 순서가 정해진다. 우리는 order_by()
메소드를 사용해 QuerySet당 기준값을 재정의할 수 있다.
'-' 마이너스 값은 내림차순으로 order 하겠다는 것이며, '?'는 랜덤으로 순서 정하겠다는 뜻이다.
또한, Query expression로 asc()
, desc()
와 함께 정렬할 수 있다.
인덱싱으로 QuerySet 수에 제한을 둘 수 있다. 해당 QuerySet과 맞지 않을 객체가 나오면 첫번째는 IndexError
, 두번째는 DoesNotExists
error가 나올 것이다.
>>> Posting.objects.all()[:3]
<QuerySet [<Posting: Posting object (2)>, <Posting: Posting object (3)>, <Posting: Posting object (4)>]>
해당 메소드는 객체 리스트를 데이터베이스에 더 효율적으로 insert한다.
>>> Posting.objects.bulk_create([Posting(image_url="https://img.com/lkj90", description="벌크 크리에이트중", username_id=1), Posting(image_url="apple.com/1234", description="New laptopppp", username_id=3)])
[<Posting: Posting object (None)>, <Posting: Posting object (None)>]
mysql을 확인해보면
mysql> mysql> select * from postings;
+----+--------------------------------+----------------------------+-----------------------------------+-------------+
| id | image_url | created_at | description | username_id |
+----+--------------------------------+----------------------------+-----------------------------------+-------------+
| 2 | https://imageurlwillchange.com | 2021-02-05 00:48:07.298620 | NULL | 1 |
| 3 | https://shorurl.com/345 | 2021-02-05 01:30:19.752123 | Invalid error must appear | 1 |
| 4 | https://pleasework.com | 2021-02-09 11:44:06.806212 | Mouse Mouse Cat Cat Tom and Jerry | 3 |
| 5 | https://image.com/1234 | 2021-02-11 02:50:45.207880 | Heyyyyyy | 3 |
| 6 | https://image.com/0987 | 2021-02-11 03:13:24.457341 | Apple Macbook Pro 16inch | 3 |
| 7 | https://abc.com/123 | 2021-02-13 13:15:16.835500 | 예시입니당 | 2 |
| 8 | https://image.com/456 | 2021-02-13 13:16:53.448512 | 다음 에시입니당 | 1 |
| 10 | https://img.com/lkj90 | 2021-02-14 04:10:43.357147 | 벌크 크리에이트중 | 1 |
| 11 | apple.com/1234 | 2021-02-14 04:10:43.357215 | New laptopppp | 3 |
+----+--------------------------------+----------------------------+-----------------------------------+-------------+
9 rows in set (0.00 sec)
#10, #11으로 들어간것을 확인할 수 있다.
QuerySet을 통해 django의 집계 함수 module를 딕셔너리 타입으로 반환한다. aggregate() 속 각 argument는 딕셔너리에 포함될 value로 포함된다. 집계도 QuerySet이기 때문에 복잡한 집계를 만들 수 있다. django에서 QuerySet의 특정 필드를 모두 더하거나, 평균, 개수 등을 구할 때 이 메소드를 쓴다.
>>> q = User.objects.aggregate(Count('username'))
>>> print(q)
{'username__count': 6}
>>> a = User.objects.aggregate(members_of_the_group=Count('username'))
>>> print(a)
{'members_of_the_group': 6}
불행히도... 해당 프로젝트에는 숫자가 포함되어있는 값들이 없어 (id값뿐..) Count만 썼다. Sum, Avg, Max, Min, 등 다른 집계함수들을 활용할 수 있다.
annotate의 사전적 의미는 주석을 단다는 뜻이다. 하지만 django에서는 주석 대신 field, 즉 칼럼을 추가한다고 생각하면 된다. .annotate()
의 각 인수는 QuerySet의 각 객체에 추가 될 주석을 가르킨다. 모델 필드에 대한 참조, QuerySet의 각 객체와 관련된 집계, 단순한 값들을 인수에 넣을 수 있다.
즉, 새로운 필드를 데이터에 추가하고 해당 필드에 어떤 내용을 채울 수 있는 기회를 제공해주는 메소드다. 먼저 데이터 수를 파악하기 위해 .count()
라는 함수를 이용해서 '주석'을 달아주고 이른 __count
으로 접근할 수 있게 된다!
>>> a = User.objects.annotate(Count('username'))
>>> a[0].email
'cnim@naver.com'
>>> a[0].username__count
1
westagram 데이터베이스가 초라하니... 값 출력이 계속 '1'이거나.. 객체가 하나만 반환된다ㅠㅠ
.aggregate()
.annotate()
의 차이점은aggregate
은 전체 QuerySet에 대한 값을 계산하고,annotate
은 QuerySet의 각 객체들에 작용된다.
Reference:
https://docs.djangoproject.com/en/3.1/ref/models/querysets/
https://docs.djangoproject.com/en/3.1/topics/db/queries/
https://velog.io/@magnoliarfsit/ReDjango-8.-QuerySet-Method-2
갓한아님 블로그 포스트 : https://devvvyang.tistory.com/37?category=973523
민지님 정리 너무 깔끔하게 잘하시네요 🤭🤭