Оптимизация запросов при работе со связанными объектами.
Проблема N+1
# Плохо: 1 запрос для задач + N запросов для каждого проекта
tasks = Task.objects.all()
for task in tasks:
print(task.project.name) # каждый раз — новый SQL запрос!
select_related — JOIN для ForeignKey/OneToOne
# Хорошо: один запрос с JOIN
tasks = Task.objects.select_related('project', 'owner')
for task in tasks:
print(task.project.name) # уже загружено
print(task.owner.username) # уже загружено
Работает для: ForeignKey, OneToOneField.
prefetch_related — отдельный запрос для ManyToMany
# Два запроса: задачи + все теги
tasks = Task.objects.prefetch_related('tags')
for task in tasks:
for tag in task.tags.all(): # уже загружено
print(tag.name)
Работает для: ManyToManyField, обратные ForeignKey через related_name.
Комбинирование
tasks = Task.objects.select_related('project', 'owner').prefetch_related('tags')
Prefetch с фильтрацией
from django.db.models import Prefetch
projects = Project.objects.prefetch_related(
Prefetch(
'tasks',
queryset=Task.objects.filter(status='todo').order_by('priority'),
to_attr='todo_tasks', # сохранить в атрибут
)
)
for project in projects:
print(project.todo_tasks) # список, не queryset
Когда что использовать
| Ситуация | Решение |
|---|---|
task.project.name (FK) |
select_related('project') |
task.tags.all() (M2M) |
prefetch_related('tags') |
project.tasks.all() (обратный FK) |
prefetch_related('tasks') |
| FK + фильтр на связанном | prefetch_related(Prefetch(...)) |
💬 Комментарии (0)
Комментариев пока нет
Станьте первым, кто поделится мнением об этой статье!