๐ค ORM์ ๊ฒ์ผ๋ฅด๋ค (Lazy Loading)
# models.py
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __str__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField(default=date.today)
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField(default=0)
number_of_pingbacks = models.IntegerField(default=0)
rating = models.IntegerField(default=5)
def __str__(self):
return self.headline
์ด๋ฐ ๋ชจ๋ธ์ ๋ง๋ค์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ migrate
ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
>>> from blog.models import Blog
>>> blog = Blog.objects.filter(name="Beatles Blog")
>>> print(blog)
์์ ๊ฐ์ด Blog ๊ฐ์ฒด๋ฅผ filtering ํ๋ Query๋ฌธ์ ๋ ๋ฆฐ๋ค๋ฉด database hits๋ ๋ช ๋ฒ ์ผ์ด๋ ๊น? ์ ๋ต์ 1๋ฒ
์ด๋ค. ๋ง์ง๋ง ์ค print(blog)
์์๋ง db hits๊ฐ ์ผ์ด๋๋ ๊ฒ์ด๋ค. Django ORM์ QuerySet์ด ํ๊ฐ(evaluated)๋ ๋๊น์ง ์ค์ ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํด ์ฟผ๋ฆฌ๋ฅผ ์คํํ์ง ์๋๋ค.
QuerySet์ด ํ๊ฐ(evaluated)๋๋ ์์
๋ด๋ถ์ ์ผ๋ก, QuerySet์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ hitting ๋์ง ์๊ณ ํํฐ๋ง๋๊ณ , ์ฌ๋ผ์ด์ฑ ๋ ์ ์๋ค. queryset์ ํ๊ฐํ๊ธฐ ์ ๊น์ง ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ๋์ ์ค์ ๋ก ์ํ๋์ง ์๋๋ค.
์ด๋จ ๋ queryset์ ํ๊ฐํ๋๊ฐ?
- ๋ฐ๋ณต (Iteration)
for b in Blog.objects.all():
print(b)
- ์ฌ๋ผ์ด์ฑ (Slicing)
blogs = Blog.objects.all()[::2]
step parameter๋ฅผ ์ธ ๋ ํ๊ฐ๋จ
- Picking / Caching
- repr()
blogs = repr(Blog.objects.all())
- len()
blogs = len(Blog.objects.all())
- list()
blogs = list(Blog.objects.all())
- bool()
if Blog.objects.filter(name="Rim's Blog"):
print("This is Rim's blog!")
๐ฆ Caching
๊ฐ QuerySet์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ์ ์ต์ํํ๊ธฐ ์ํ ์บ์๊ฐ ํฌํจ๋์ด ์๋ค. ์ด ์๋ ๋ฐฉ์์ ์ดํดํ๋ฉด ํจ์จ์ ์ธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ค.
QuerySet์ด ์ฒ์ ํ๊ฐ๋ ๋, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๋ค. Django๋ ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ๋ฅผ ์บ์์ ์ ์ฅํ๊ณ ์์ฒญ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค. QuerySet์ ๋ค์ ํ๊ฐ๋ ์บ์์ ์ ์ฅ๋ ๊ฒฐ๊ณผ๋ฅผ ์ฌ์ฌ์ฉํ๋ค.
๐ ์บ์ฑ์ ์ ์ฉํ์ง ์์์ ๋
for blog in Blog.objects.all(): # ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ
print(blog.name)
for blog in Blog.objects.all(): # ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ค์ ์ฟผ๋ฆฌ
print(blog.tagline)
์ ์ฝ๋๋ ๋ ๊ฐ์ QuerySet์ด ํ๊ฐ๋๋ค.
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog"; args=(); alias=default
Beatles Blog
Rim's Blog
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog"; args=(); alias=default
All the latest Beatles news.
This is the Rim's blog.
์ด ๋ ์บ์ฑ์ ์ด์ฉํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ๋ฌธ์ ์ค์ผ ์ ์๋ค.
๐ ์บ์ฑ์ ์ ์ฉํ์ ๋
blogs = Blog.objects.all() # ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ
for blog in blogs: # ์บ์ ์ฌ์ฉ
print(blog.name)
for blog in blogs: # ์บ์ ์ฌ์ฉ
print(blog.tagline)
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog"; args=(); alias=default
Beatles Blog
Rim's Blog
All the latest Beatles news.
This is the Rim's blog.
์ ์ฒ๋ผ queryset์ด ํ๊ฐ๋ ๋ ์บ์์ query์ ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๊ธฐ ๋๋ฌธ์ ์ฟผ๋ฆฌ๋ฌธ์ด ํ ๋ฒ๋ง ๋ ์๊ฐ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
QuerySet์ด ์บ์๋์ง ์์ ๋
QuerySet์ด ํญ์ ๊ฒฐ๊ณผ๋ฅผ ์บ์ํ๋ ๊ฒ์ ์๋๋ค. slice๋ index๋ก queryset์ ์ ํํ๋ฉด ์บ์๊ฐ ๋์ง ์๋๋ค.
blogs = Blog.objects.all()
print(blogs[0]) # ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ
print(blogs[0]) # ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ค์ ์ฟผ๋ฆฌ
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog" LIMIT 1; args=(); alias=default
Beatles Blog
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog" LIMIT 1; args=(); alias=default
Beatles Blog
์์ฒ๋ผ queyset object์์ ํน์ ์ธ๋ฑ์ค๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ๊ฐ์ ธ์ค๋ฉด ๋งค๋ฒ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฟผ๋ฆฌํ๋ค.
blogs = Blog.objects.all()
for blog in blogs: # ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ
print(blog)
print(blogs[0]) # ์บ์ ์ฌ์ฉ
print(blogs[0]) # ์บ์ ์ฌ์ฉ
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog"; args=(); alias=default
Beatles Blog
Rim's Blog
Beatles Blog
Beatles Blog
๊ทธ๋ฌ๋ ์์ฒ๋ผ queryset์ด ์ด๋ฏธ ํ๊ฐ๋ ๊ฒฝ์ฐ, ์บ์๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
๐ EagerLoading (Reslove N+1 Problem)
ORM์ ๊ฒ์ผ๋ฅด๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ์ ์ผ๋ก Lazy Loading ์ ๋ต์ ํํ๋ค ๐ช. ํ์ง๋ง Eager Loading ์ ์ด์ฉํ๋ฉด ์ฆ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
N+1 Problem
class ExampleView(View):
def get(self, request):
entrys = Entry.objects.all()
blog_name_list = [entry.blog for entry in entrys]
return HttpResponse(blog_name_list)
(0.000) SELECT "blog_entry"."id", "blog_entry"."blog_id", "blog_entry"."headline", "blog_entry"."body_text", "blog_entry"."pub_date", "blog_entry"."mod_date", "blog_entry"."number_of_comments", "blog_entry"."number_of_pingbacks", "blog_entry"."rating" FROM "blog_entry"; args=(); alias=default
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog" WHERE "blog_blog"."id" = 2 LIMIT 21; args=(2,); alias=default
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog" WHERE "blog_blog"."id" = 2 LIMIT 21; args=(2,); alias=default
๋ง์ฝ Entry ๋ชจ๋ธ์ ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ๋ถ๋ฌ์จ ํ ๊ฐ ๊ฐ์ฒด์ ์์ฑ blog๋ฅผ ๊ฐ์ ธ์ค๊ฒ ๋๋ฉด blog๋ Blog ๋ชจ๋ธ๊ณผ Foreignkey๋ฅผ ํตํด์ ์ฐ๊ฒฐ๋ ์์ฑ์ด๊ธฐ ๋๋ฌธ์ Blog ๊ฐ์ฒด๋ฅผ ๊ฒ์ฌํ๋ query๋ฌธ์ด for๋ฌธ์ด ๋์๊ฐ ๋๋ง๋ค ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ ์๊ฐ๊ฒ ๋๋ค.
์ด๋ ๊ฒ ์ผ์ผ์ด query๋ฌธ์ ๋ณด๋ด๋ ์ด์ ๋ ORM์ ํน์ง์ธ Lazy Loading ๋๋ฌธ์ด๋ค. ORM์ Entry์ ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ๊ฒ์ฌํ๋ query๋ฌธ์ ๋ณด๋ด๊ณ for ๋ฌธ ์์์ entry์ blog ์ ๋ณด๋ฅผ ์ฐพ๋๋ค. ํ์ง๋ง ์ฒซ๋ฒ์งธ query๋ฌธ์์ ์ํ๋ ์ ๋ณด๋ฅผ ์ฐพ์ง ๋ชปํ๋ฉด ๋ค์ query๋ฌธ์ ๋ณด๋ด์ด ์ํ๋ ์ ๋ณด๋ฅผ ์ฐพ๊ฒ๋๋ค. ORM์ ํ์ํ ๋๋ง๋ค query๋ฌธ์ ๋ณด๋ด๊ธฐ ๋๋ฌธ์ด๋ค.
์ด ์ถ๊ฐ query๋ฌธ์ ๊ฐ์ฒด๋ฅผ ๊ฒ์ฌํ ๋๋ง๋ค ๋ ์๊ฐ๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด์ ์๊ฐ ๋์ด๋๋ฉด ๋ ๋ง์ query๋ฌธ์ด ๋ ์๊ฐ๊ฒ ๋๋ค.
(0.000) SELECT "blog_entry"."id", "blog_entry"."blog_id", "blog_entry"."headline", "blog_entry"."body_text", "blog_entry"."pub_date", "blog_entry"."mod_date", "blog_entry"."number_of_comments", "blog_entry"."number_of_pingbacks", "blog_entry"."rating" FROM "blog_entry"; args=(); alias=default
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog" WHERE "blog_blog"."id" = 2 LIMIT 21; args=(2,); alias=default
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog" WHERE "blog_blog"."id" = 2 LIMIT 21; args=(2,); alias=default
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog" WHERE "blog_blog"."id" = 1 LIMIT 21; args=(1,); alias=default
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog" WHERE "blog_blog"."id" = 2 LIMIT 21; args=(2,); alias=default
์์ฒ๋ผ 2๊ฐ์ Entry ๊ฐ์ฒด๋ฅผ ์ถ๊ฐํ์ฌ ๋ฃ์๋๋ ๋ ๊ฐ์ ์ถ๊ฐ query๋ฌธ์ด ๋ ์๊ฒจ๋ฌ๋ค. ๋ง์ฝ 100๊ฐ, 1000๊ฐ์ ๊ฐ์ฒด๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์กด์ฌํ๋ค๋ฉด, query๋ฌธ์ ๊ฐฏ์๋ ๋๊ฐ์ด ๋์ด๋ ์๋ฒ ์คํ ์๋๋ ๋๋ ค์ง ๊ฒ์ด๋ค. ๐คข
QuerySet์ ๊ตฌ์ฑ์์
# ์ค์ django.db.models.query.py์ ์๋ QuerySet์ ๊ตฌ์ฑ์์
from django.db.model.sql import Query # Query()๋ ๊ฐ๋ฐ์๊ฐ ์ ์ํ QuerySet์ ์ฝ์ด์ ์ค์ SQL์ ์์ฑํด์ฃผ๋ ๊ตฌํ์ ์ด๋ค.
class QuerySet:
query: Query = Query() # ๋ฉ์ธ ์ฟผ๋ฆฌ
_result_cache: List[Dict[Any, Any]] = dict() # SQL์ ์ํ๊ฒฐ๊ณผ๋ฅผ ์ฌ๊ธฐ ์ ์ฅํด๋๊ณ ์ฌ์ฌ์ฉํ๋ค. (QuerySet Cache)
# QuerySet ํธ์ถ ์ ์ด ํ๋กํผํฐ์ ์ ์ฅ๋ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด SQL์ ํธ์ถํด์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค.
_prefetch_related_lookups: Tuple[str] = () # prefetch_related์ ์ด ํ๋กํผํฐ์ ์ ์ฅํ๋ค.
# ์ถ๊ฐ ์ฟผ๋ฆฌ์
์์ N+1 Problem์์ ํ์ธํ๋ ๊ฒ์ฒ๋ผ QuerySet์ 1๊ฐ์ ์ฟผ๋ฆฌ์ 0 ~ N๊ฐ์ ์ถ๊ฐ์ฟผ๋ฆฌ(์ )์ผ๋ก ๊ตฌ์ฑ๋์ด ์๋ค. ์๋ ์ค์ QuerySet์ด ์๋ํ๋ ์ฝ๋์ด๋ค. ๋ฉ์ธ์ฟผ๋ฆฌ์ ์ถ๊ฐ ์ฟผ๋ฆฌ์ ์ผ๋ก ์ด๋ฃจ์ด์ ธ ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
โ๏ธ select_related() & prefetch_related()
Eager Loading์ ์ํํ๊ธฐ ์ํ ์ต์ ์ธ seleted_related์ prefetch_related์ ๋ํด ์์๋ณด์.
1) select_related()
select_related
๋ join๋ฌธ์ ํตํด์ ๋ฐ์ดํฐ๋ฅผ ์ฆ์ ๋ก๋ฉํ๋ค. ์์์ ์ํํ N+1 Problem
์ด ๋ฐ์ํ ์ฝ๋๋ฅผ select_related
๋ฅผ ํตํด์ ์ถ๊ฐ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ์ง ์๋๋ก ์์ ์ ํ ์ ์๋ค.
class BlogDetailView(View):
def get(self, request):
entrys = Entry.objects.all().select_related("blog")
blog_name_list = [entry.blog for entry in entrys]
return HttpResponse(blog_name_list)
(0.000) SELECT "blog_entry"."id", "blog_entry"."blog_id", "blog_entry"."headline", "blog_entry"."body_text", "blog_entry"."pub_date", "blog_entry"."mod_date", "blog_entry"."number_of_comments", "blog_entry"."number_of_pingbacks", "blog_entry"."rating", "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_entry" INNER JOIN "blog_blog" ON ("blog_entry"."blog_id" = "blog_blog"."id"); args=(); alias=default
์์ ๊ฐ์ด ์ถ๊ฐ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๋ ์์ฑ blog๋ฅผ select_related
๋ฅผ ํตํด ๊ฐ์ ธ์ค๋ฉด ์ถ๊ฐ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ์ง ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค. INNER JOIN
์ ์ฌ์ฉํ์ฌ blog ํ
์ด๋ธ์ entry ํ
์ด๋ธ๊ณผ ํจ๊ป ๋ถ๋ฌ์ ์ถ๊ฐ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ์ง ์๋๋ก ํด์ฃผ๋ ๊ฒ์ด๋ค.
2) prefetch_related()
class BlogDetailView(View):
def get(self, request):
entrys = Entry.objects.prefetch_related("blog").all()
blog_name_list = [entry.blog for entry in entrys]
return HttpResponse(blog_name_list)
(0.000) SELECT "blog_entry"."id", "blog_entry"."blog_id", "blog_entry"."headline", "blog_entry"."body_text", "blog_entry"."pub_date", "blog_entry"."mod_date", "blog_entry"."number_of_comments", "blog_entry"."number_of_pingbacks", "blog_entry"."rating" FROM "blog_entry"; args=(); alias=default
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog" WHERE "blog_blog"."id" IN (1, 2); args=(1, 2); alias=default
prefetch_related
๋ฅผ ์ฌ์ฉํ๋ฉด ํ๋์ ์ถ๊ฐ ์ฟผ๋ฆฌ๋ฌธ์ด ๋ ๋ ์๊ฐ๊ฒ๋๋ค. ์ด๋ ๊ฒ prefetch_related
๋ ์ถ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ํตํด์ ์ฐธ์กฐํ๊ณ ์๋ ํ
์ด๋ธ์ ์ ๋ณด๋ฅผ ์ ๋ถ ๊ฐ์ ธ์ค๊ฒ ๋๋ค.
๐ค ๊ทธ๋ผ select_related()์ prefetch_related ์ค ์ด๋ค ๊ฒ์ ์ฐ๋ฉด ์ข์๊น?
select_related
๋ foreignkey
๋ฐ 1:1 ๊ด๊ณ์์ ์ฌ์ฉ๋๋๋ก ์ ํ๋๋ค. ๊ทธ์ ๋ฐํด prefetch_related
๋ ๋ค๋๋ค ๊ด๊ณ ๊ด๊ณ์์ ์ฌ์ฉ๋ ์ ์๋ค.
class BlogMainView(View):
def get(self, request, blog_id):
blog = Blog.objects.get(id=blog_id)
entrys = Entry.objects.filter(blog_id=blog_id)
results = [
{
"blog_name": blog.name,
"blog_tagline": blog.tagline,
"entry": [
{
"headline": entry.headline,
"body_text": entry.body_text,
"authors": [
{"author": entry_author.name}
for entry_author in entry.authors.all()
],
}
for entry in entrys
],
}
]
return JsonResponse({"result": results}, status=200)
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog" WHERE "blog_blog"."id" = 1 LIMIT 21; args=(1,); alias=default
(0.000) SELECT "blog_entry"."id", "blog_entry"."blog_id", "blog_entry"."headline", "blog_entry"."body_text", "blog_entry"."pub_date", "blog_entry"."mod_date", "blog_entry"."number_of_comments", "blog_entry"."number_of_pingbacks", "blog_entry"."rating" FROM "blog_entry" WHERE "blog_entry"."blog_id" = 1; args=(1,); alias=default
(0.000) SELECT "blog_author"."id", "blog_author"."name", "blog_author"."email" FROM "blog_author" INNER JOIN "blog_entry_authors" ON ("blog_author"."id" = "blog_entry_authors"."author_id") WHERE "blog_entry_authors"."entry_id" = 1; args=(1,); alias=default
(0.000) SELECT "blog_author"."id", "blog_author"."name", "blog_author"."email" FROM "blog_author" INNER JOIN "blog_entry_authors" ON ("blog_author"."id" = "blog_entry_authors"."author_id") WHERE "blog_entry_authors"."entry_id" = 2; args=(2,); alias=default
(0.000) SELECT "blog_author"."id", "blog_author"."name", "blog_author"."email" FROM "blog_author" INNER JOIN "blog_entry_authors" ON ("blog_author"."id" = "blog_entry_authors"."author_id") WHERE "blog_entry_authors"."entry_id" = 3; args=(3,); alias=default
์ ์ฝ๋๋ฅผ ๋ณด๋ฉด ์ถ๊ฐ query๊ฐ ๋ฐ์ํด N+1 Problem
์ด ๋ฐ์ํ๊ณ ์๋ค.
class BlogMainView(View):
def get(self, request, blog_id):
blog = Blog.objects.get(id=blog_id)
entrys = Entry.objects.prefetch_related("authors").filter(blog_id=blog_id)
results = [
{
"blog_name": blog.name,
"blog_tagline": blog.tagline,
"entry": [
{
"headline": entry.headline,
"body_text": entry.body_text,
"authors": [
{"author": entry_author.name}
for entry_author in entry.authors.all()
],
}
for entry in entrys
],
}
]
return JsonResponse({"result": results}, status=200)
(0.000) SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" FROM "blog_blog" WHERE "blog_blog"."id" = 1 LIMIT 21; args=(1,); alias=default
(0.000) SELECT "blog_entry"."id", "blog_entry"."blog_id", "blog_entry"."headline", "blog_entry"."body_text", "blog_entry"."pub_date", "blog_entry"."mod_date", "blog_entry"."number_of_comments", "blog_entry"."number_of_pingbacks", "blog_entry"."rating" FROM "blog_entry" WHERE "blog_entry"."blog_id" = 1; args=(1,); alias=default
(0.000) SELECT ("blog_entry_authors"."entry_id") AS "_prefetch_related_val_entry_id", "blog_author"."id", "blog_author"."name", "blog_author"."email" FROM "blog_author" INNER JOIN "blog_entry_authors" ON ("blog_author"."id" = "blog_entry_authors"."author_id") WHERE "blog_entry_authors"."entry_id" IN (1, 2, 3); args=(1, 2, 3); alias=default
prefetch_related
๋ฅผ ์ฌ์ฉํ๋ฉด ์ฟผ๋ฆฌ์ ์๋ฅผ ์ค์ผ ์ ์๋ค. ํ์ง๋ง ์ฌ๊ธฐ์ select_related
๋ฅผ ์ฌ์ฉํ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํ๋ค. select_related
๋ ๋ค๋๋ค ๊ด๊ณ์์ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๐๐ปโ๏ธ : ๊ทธ๋์ ์ด๋ค๊ฑธ ์ฌ์ฉํ๋ผ๊ณ ์.
๐
๊ฒฐ๋ก : ์ผ๋์ผ๊ด๊ณ๋ ์ ์ฐธ์กฐ ๊ด๊ณ์์๋ select_related
๋ฅผ ์ฌ์ฉํ๊ณ , ๋ค๋๋ค๊ด๊ณ๋ ์ญ์ฐธ์กฐ ๊ด๊ณ์์๋ prefetch_related
๋ฅผ ์ฌ์ฉํ์. ๋ง์ฝ select_related
๋ฅผ ์ฌ์ฉํ ์ ์๋ ์ํฉ์ด๋ผ๋ฉด, query์ ์๊ฐ ํ๋ ๋ ์ค์ด๋๋ ์ ๋งํ๋ฉด select_related
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค!
<์ฐธ๊ณ >
'Python > ๐ฆ Django' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Django] DRF ์๋ ํ๋ฆ (0) | 2023.11.23 |
---|---|
[Django] ์ฝ๋ฉ ์คํ์ผ (0) | 2023.09.04 |
[Django] Django Custom Command (0) | 2023.09.04 |
[Django] JWT๋ก ๋ก๊ทธ์์์ ๊ตฌํํด๋ณด์ (0) | 2023.09.04 |
[Django] Django ORM์ด๋ ๋ฌด์์ผ๊น? (0) | 2021.10.30 |