Предзагрузка связей prefetch_related в Django
Метод select_related хорошо
работает, когда у записи одна
связанная запись: категория у поста,
миниатюра у поста. Но он не
подходит для обратной стороны
ForeignKey и для
ManyToManyField: там
связанных записей может быть много.
Для таких случаев есть
prefetch_related. Он делает
отдельный запрос за связанными
объектами и затем склеивает их
в Python.
Пусть у нас снова категории и посты:
categories
- id
- name
posts
- id
- title
- category_id
Без оптимизации получение постов для каждой категории снова дает проблему N+1:
from main.models import Category
categories = Category.objects.all()
for category in categories:
print(category.name)
for post in category.posts.all():
print(' -', post.title)
Давайте подгрузим посты заранее
через prefetch_related.
В скобках передают имя обратной
связи (здесь posts из
related_name):
categories = Category.objects.prefetch_related('posts')
for category in categories:
print(category.name)
for post in category.posts.all():
print(' -', post.title)
Теперь Django сделает два запроса: один за категориями и один за всеми нужными постами. В цикле лишних запросов не будет.
То же работает для many-to-many. Пусть посты связаны с тегами:
posts
- id
- title
tags
- id
- name
Подгрузим теги заранее - передадим имя поля связи:
from main.models import Post
posts = Post.objects.prefetch_related('tags')
for post in posts:
print(post.title)
for tag in post.tags.all():
print(' -', tag.name)
Получите все страны вместе
с городами через
prefetch_related.
Получите всех студентов вместе
с курсами через
prefetch_related.
Расскажите, чем
prefetch_related
отличается от
select_related
и когда какой метод использовать.