select_related() для связей ForeignKey и OneToOneField, а prefetch_related() — для ManyToManyField и обратных связей.
select_related() для связей ForeignKey и OneToOneField, а prefetch_related() — для ManyToManyField и обратных связей.
Что такое N+1
Проблема N+1 появляется, когда приложение получает список из N объектов одним запросом, а затем в цикле обращается к связанным данным. Тогда Django выполняет ещё N запросов: по одному для каждого объекта.
articles = Article.objects.all()
for article in articles:
print(article.author.username)Если у статьи есть связь с автором, такой код может выполнить один запрос за статьями и отдельный запрос за автором каждой статьи.
select_related()
select_related() обычно используют для ForeignKey и OneToOneField. Django получает основные и связанные объекты через SQL JOIN в одном запросе.
articles = Article.objects.select_related('author')prefetch_related()
prefetch_related() подходит для ManyToManyField, обратных ForeignKey и других связей, где JOIN может раздувать результат. Django выполняет отдельные запросы для связанных объектов и сопоставляет их в Python.
articles = Article.objects.prefetch_related('tags', 'comments')Как найти проблему
Количество и длительность SQL-запросов удобно проверять через Django Debug Toolbar, тесты с подсчётом запросов или логи базы данных. Оптимизацию стоит применять после измерения, а не добавлять все prefetch заранее.
Как ответить на собеседовании
N+1 — это один запрос за списком объектов и N запросов за связанными данными. Для связей один-к-одному и многие-к-одному я использую select_related(), а для многие-ко-многим и обратных связей — prefetch_related().
Оцени свой прогресс