Ответ
Prefetch
— это объект в Django ORM, который используется для кастомизации поведения метода prefetch_related()
. Он позволяет выполнять предварительную загрузку связанных объектов с применением отдельного QuerySet
, что дает полный контроль над фильтрацией, сортировкой и выбором полей для связанных данных.
Зачем это нужно?
Стандартный вызов Article.objects.prefetch_related('comments')
загрузит все комментарии для каждой статьи одним дополнительным запросом. Если вам нужны не все, а только определенные комментарии (например, только активные), Prefetch
становится незаменимым инструментом.
Как это работает
Объект Prefetch
принимает два основных аргумента:
lookup
: Имя связанного поля для предварительной загрузки.queryset
:QuerySet
, который будет использоваться для выборки связанных объектов.
Пример: загрузка только опубликованных комментариев
from django.db.models import Prefetch
from .models import Article, Comment
# Загружаем статьи и для каждой из них — только активные комментарии
articles = Article.objects.prefetch_related(
Prefetch(
'comments', # Поле для prefetch
queryset=Comment.objects.filter(is_published=True), # Кастомный queryset
)
)
for article in articles:
# article.comments.all() будет содержать только отфильтрованные комментарии
print(f"Статья: {article.title}")
for comment in article.comments.all():
print(f" - {comment.text}")
Дополнительные возможности
С помощью аргумента to_attr
можно сохранить результат в новый атрибут, не перезаписывая стандартный менеджер связей.
# Результат будет сохранен в article.published_comments
articles = Article.objects.prefetch_related(
Prefetch(
'comments',
queryset=Comment.objects.filter(is_published=True),
to_attr='published_comments'
)
)
for article in articles:
# article.comments.all() по-прежнему вернет все комментарии (если сделать новый запрос)
# article.published_comments содержит отфильтрованный и предзагруженный список
for comment in article.published_comments:
print(comment.text)
Важно: Prefetch
работает только с prefetch_related
(который выполняет отдельный SQL-запрос), а не с select_related
(который использует JOIN
).