Ответ
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).
Ответ 18+ 🔞
Давай разберём этот ваш Prefetch, а то смотрю, народ путается, как хуй с винтом. Представь себе, что у тебя есть статьи и комментарии. Стандартный prefetch_related('comments') — это как прийти в бар и сказать: «Налей всё, что есть!». Тебе принесут и пиво, и водку, и какую-то хуйню с персиком, которую никто не заказывал.
А Prefetch — это когда ты говоришь: «Слушай, брат, налей-ка мне, но только пива, и чтобы оно было холодное, и чтобы с персиком не было, а то у меня на него аллергия, в рот меня чих-пых». То есть ты кастомизируешь запрос, фильтруешь эту лабуду.
Вот смотри, как это выглядит в коде, если тебе нужны только опубликованные комментарии:
from django.db.models import Prefetch
from .models import Article, Comment
# Берём статьи, но комментарии подгружаем не все подряд, а только те, что прошли модерацию
articles = Article.objects.prefetch_related(
Prefetch(
'comments', # Смотри сюда, это поле, по которому связь
queryset=Comment.objects.filter(is_published=True), # А вот тут наш фильтр, отсеиваем шлак
)
)
for article in articles:
print(f"Статья: {article.title}")
# И вот тут, охуеть, в article.comments.all() уже лежат только отфильтрованные комменты
for comment in article.comments.all():
print(f" - {comment.text}")
А ещё есть прикол с to_attr. Это вообще магия, блядь. Допустим, ты не хочешь трогать стандартный менеджер comments. Ну мало ли, вдруг он тебе ещё для чего-то нужен? Тогда ты делаешь так:
# Результат летит не в article.comments, а в отдельный карман — 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. Это когда делается отдельный запрос. С select_related, который через JOIN всё склеивает, эта штука не прокатит. Не путай, а то будешь выглядеть, как манда с ушами.