Django QuerySet
1. QuerySet์ด๋?
DB์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ๋ ์ฝ๋ ์งํฉ (๋ฆฌ์คํธ์ฒ๋ผ ์ฌ์ฉ ๊ฐ๋ฅ)
Post.objects.all() # QuerySet ๊ฐ์ฒด ๋ฐํ
Python
๋ณต์ฌ
QuerySet์ ํต์ฌ ํน์ง
1.
์ง์ฐ ํ๊ฐ (Lazy Evaluation)
โข
QuerySet์ ์ ์ํ๋ ์์ ์๋ DB์ ์ ๊ทผํ์ง ์์
โข
์ค์ ๋ก ๊ฒฐ๊ณผ๊ฐ ํ์ํ ์์ (์ดํฐ๋ ์ด์
, ์ฌ๋ผ์ด์ฑ ๋ฑ)์ ์ฟผ๋ฆฌ ์คํ
2.
์ฒด์ด๋ ๊ฐ๋ฅ (Chainable)
โข
์ฌ๋ฌ ๋ฉ์๋๋ฅผ ์ฐ๊ฒฐํด์ ์ฌ์ฉ ๊ฐ๋ฅ
โข
๊ฐ ๋จ๊ณ๋ง๋ค ์๋ก์ด QuerySet ๋ฐํ
3.
๋ฐ๋ณต ๊ฐ๋ฅ (Iterable)
โข
for ๋ฌธ์ผ๋ก ์ํ ๊ฐ๋ฅ
โข
๋ฆฌ์คํธ์ฒ๋ผ ์ธ๋ฑ์ฑ/์ฌ๋ผ์ด์ฑ ์ง์
4.
์บ์ (Caching)
โข
ํ ๋ฒ ์คํ๋ QuerySet์ ๋ฉ๋ชจ๋ฆฌ์ ์บ์
โข
๋์ผํ ์ฟผ๋ฆฌ ์ฌ์คํ ์ DB ์ ๊ทผ ์์ด ์บ์ ํ์ฉ
2. QuerySet ์ฃผ์ ๋ฉ์๋ ์์ฝ
๋ฉ์๋ | ์ค๋ช
|
.all() | ์ ์ฒด ๋ ์ฝ๋ ์กฐํ |
.filter(์กฐ๊ฑด) | ์กฐ๊ฑด์ ๋ง๋ ๋ ์ฝ๋ ์ถ์ถ |
.get(์กฐ๊ฑด) | ์กฐ๊ฑด์ ํด๋นํ๋ ํ๋์ ๋ ์ฝ๋๋ง (์๊ฑฐ๋ ์ฌ๋ฌ ๊ฐ๋ฉด ์๋ฌ) |
.exclude(์กฐ๊ฑด) | ์กฐ๊ฑด์ ์ ์ธํ ๋ ์ฝ๋ |
.order_by('field') | ์ ๋ ฌ (-ํ๋๋ช
์ผ๋ก ๋ด๋ฆผ์ฐจ์) |
.count() | ์ด ๊ฐ์ |
.exists() | ์กด์ฌ ์ฌ๋ถ ๋ฐํ (Boolean) |
.first(), .last() | ์ฒซ ๋ฒ์งธ/๋ง์ง๋ง ๊ฐ์ฒด ๋ฐํ |
.values(), .values_list() | dict ๋๋ tuple๋ก ์ถ์ถ |
3. ํํฐ๋ง ์์ (Field lookup)
Post.objects.filter(title__contains="์ฅ๊ณ ") # ๋ถ๋ถ ํฌํจ
Post.objects.filter(title__startswith="๊ณต์ง") # ์์ ๋จ์ด
Post.objects.filter(title__iexact="๊ณต์ง์ฌํญ") # ๋์๋ฌธ์ ๋ฌด์
Post.objects.filter(id__in=[1, 2, 3]) # ๋ชฉ๋ก ์กฐ๊ฑด
Post.objects.filter(created_at__date='2024-01-01') # ๋ ์ง ์กฐ๊ฑด
Python
๋ณต์ฌ
4. ์ ๋ ฌ
Post.objects.order_by('created_at') # ์ค๋ฆ์ฐจ์
Post.objects.order_by('-created_at') # ๋ด๋ฆผ์ฐจ์
Python
๋ณต์ฌ
5. ์ฌ๋ผ์ด์ฑ
Post.objects.all()[:5] # ์ 5๊ฐ
Post.objects.all()[5:10] # 6~10๋ฒ์งธ
Python
๋ณต์ฌ
์ฃผ์: ์ฌ๋ผ์ด์ฑ์ ์ค์ ๋ก LIMIT OFFSET SQL์ด ์คํ๋จ
6. ์ง๊ณ (Aggregation)
from django.db.models import Count, Avg, Max
Post.objects.aggregate(Count('id')) # ์ด ๊ฐ์
Post.objects.aggregate(Max('created_at')) # ์ต์ ๋ ์ง
Python
๋ณต์ฌ
7. ๊ทธ๋ฃน๋ณ ์ง๊ณ (annotate)
from django.db.models import Count
# ๊ฒ์๊ธ๋ณ ๋๊ธ ์ ์ถ๋ ฅ
Post.objects.annotate(num_comments=Count('comment')).values('title', 'num_comments')
Python
๋ณต์ฌ
8. Q ๊ฐ์ฒด (๋ณต์กํ ์กฐ๊ฑด)
from django.db.models import Q
Post.objects.filter(Q(title__contains="์ฅ๊ณ ") | Q(content__contains="Django"))
Post.objects.filter(Q(title__contains="๊ณต์ง") & Q(is_public=True))
Python
๋ณต์ฌ
9. ์ค์ต ์์
์ค์ต๋ช
| ๋ชฉํ |
๊ฒ์๊ธ ๊ฒ์ | filter(title__contains=) ํ์ฉ |
์ ๋ ฌ ๋ฐ ํ์ด์ง | order_by() + ์ฌ๋ผ์ด์ฑ ์ค์ต |
๋๊ธ ์ ์ง๊ณ | annotate(Count())๋ก ์ถ๋ ฅ |
๋ค์ค ์กฐ๊ฑด ๊ฒ์ | Q() ๊ฐ์ฒด ์ฌ์ฉ ์ค์ต |
10. shell์์ ์ฐ์ต
python manage.py shell
Shell
๋ณต์ฌ
from board.models import Post
Post.objects.filter(title__contains="์ฅ๊ณ ")
Python
๋ณต์ฌ
QuerySet ์ฒด์ด๋
Post.objects.filter(is_public=True).order_by('-created_at')[:5]
Python
๋ณต์ฌ
QuerySet์ ์ฒด์ด๋์ผ๋ก ์กฐํฉ ๊ฐ๋ฅ โ SQL ์ฟผ๋ฆฌ ์ต์ ํ ์๋ ์ ์ฉ๋จ
์์ฝ
๊ธฐ๋ฅ | ์ฝ๋ ์์ |
ํํฐ๋ง | filter(field__lookup=value) |
์ ๋ ฌ | order_by('field'), -field |
๊ฐ์ ์ธ๊ธฐ | count(), aggregate(Count('id')) |
๋ค์ค ์กฐ๊ฑด | filter(Q(...)) |
๊ฐ๋ง ์ถ์ถ | values(), values_list() |
์กฐ์ธ
โข
๋จ์ผ ๊ฐ์ฒด ์กฐ์ธ
โฆ
1:1 = post : user (๊ฒ์๊ธ : ํ์)
โข
๋ค์ ๊ฐ์ฒด ์กฐ์ธ
โฆ
1:N = post : comment (๊ฒ์๊ธ : ๋๊ธ)
1. select_related() โ ๋จ์ผ ๊ฐ์ฒด ์กฐ์ธ (1:1, N:1)
โข
ForeignKey, OneToOneField ๊ด๊ณ์์ ์ฌ์ฉ
โข
SQL์ INNER JOIN๊ณผ ์ ์ฌํ๊ฒ ์๋
์์
comments = Comment.objects.select_related('post', 'writer')
for c in comments:
print(c.content, c.post.title, c.writer.username)
Python
๋ณต์ฌ
โข
Comment โ Post, User ๋ฅผ JOIN
โข
ํจ์จ์ ์ผ๋ก 1๊ฐ์ SQL ์ฟผ๋ฆฌ๋ก ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ด
2. prefetch_related() โ ๋ค์ ๊ฐ์ฒด ์กฐ์ธ (1:N, M:N)
โข
์ญ์ฐธ์กฐ๋ ManyToManyField์ ์ฌ์ฉ
โข
์ฌ๋ฌ ๊ฐ์ ๊ด๋ จ ๊ฐ์ฒด๊ฐ ์์ ๋ ์ ํฉ (2๊ฐ์ ์ฟผ๋ฆฌ๋ฅผ ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉ)
์์
posts = Post.objects.prefetch_related('comment_set')
for post in posts:
print(f'{post.title}์ ๋๊ธ๋ค:')
for comment in post.comment_set.all():
print(f' - {comment.content}')
Python
๋ณต์ฌ
โข
Post โ Comment JOIN
โข
์ญ์ฐธ์กฐ๋ related_name์ด ์๋ค๋ฉด <๋ชจ๋ธ๋ช
>_set์ผ๋ก ์ ๊ทผ
3. ํํฐ์์ JOIN
โข
๊ด๊ณ ํ๋๋ฅผ __(๋๋ธ ์ธ๋์ค์ฝ์ด)๋ก ๋ฐ๋ผ๊ฐ๋ฉด์ ์กฐ๊ฑด ์ค์
์์
# 'tester' ์ ์ ๊ฐ ์์ฑํ ๊ฒ์๊ธ
Post.objects.filter(writer__username='tester')
# ๊ฒ์๊ธ ์ ๋ชฉ์ 'Django' ํฌํจ๋ ๋๊ธ
Comment.objects.filter(post__title__contains='Django')
Python
๋ณต์ฌ
4. SQL๋ก JOIN ํ์ธํ๊ธฐ
qs = Comment.objects.select_related('post', 'writer')
print(qs.query)
Python
๋ณต์ฌ
โข
์ค์ SQL JOIN ์ฟผ๋ฆฌ ํ์ธ ๊ฐ๋ฅ
์์ฝ ํ
๋ชฉ์ | ๋ฉ์๋ | ๊ด๊ณ | ์ฟผ๋ฆฌ ์ | ์ค๋ช
|
์ธ๋ํค/๋จ์ผ ๊ฐ์ฒด ์กฐ์ธ | select_related() | 1:1, N:1 | 1 | JOIN์ผ๋ก ํจ๊ป ์กฐํ |
๋ค๋๋ค/์ญ์ฐธ์กฐ ์กฐ์ธ | prefetch_related() | 1:N, M:N | 2 | ๊ด๋ จ ๊ฐ์ฒด๋ฅผ ๋ณ๋ ์ฟผ๋ฆฌ๋ก ๊ฐ์ ธ์ ์กฐ์ธ |
์กฐ๊ฑด์์ JOIN | filter(FK__ํ๋) | ๋ชจ๋ | 1 | ํํฐ ์์์ ์กฐ์ธ ์ํ |
SQL ํ์ธ | .query | - | - | SQL ์ฟผ๋ฆฌ ์ง์ ํ์ธ |
์ถ๊ฐ ์์: Post - Comment ๋ชจ๋ธ
# Post โ Comment (์ญ์ฐธ์กฐ)
posts = Post.objects.prefetch_related('comment_set')
# Comment โ Post (์ ์ฐธ์กฐ)
comments = Comment.objects.select_related('post')
Python
๋ณต์ฌ
ํ์ํ์๋ฉด ManyToMany, GenericForeignKey, annotate()๋ฅผ ํ์ฉํ ์ง๊ณ JOIN ์์ ๋ ์ ๊ณตํด๋๋ฆด ์ ์์ด์.