Django ORM: select_related vs prefetch_related
В чём разница
Оба метода решают проблему N+1 запросов, но работают по-разному. select_related делает JOIN на уровне SQL, prefetch_related — отдельный запрос с WHERE id IN (...).
select_related
Используй для ForeignKey и OneToOne — там всегда один объект на другой стороне:
# Один SQL запрос с JOIN
notes = Note.objects.select_related('category').all()
for note in notes:
print(note.category.name) # нет дополнительных запросов
prefetch_related
Для ManyToMany и обратных ForeignKey — там может быть много объектов:
# Два SQL запроса: один для notes, один для tags
notes = Note.objects.prefetch_related('tags').all()
for note in notes:
print([t.name for t in note.tags.all()]) # нет N+1
Комбинирование
Можно и нужно комбинировать:
notes = Note.objects.select_related('category').prefetch_related('tags')
Когда что выбирать
- ForeignKey → select_related
- ManyToMany → prefetch_related
- Обратный FK (related_name) → prefetch_related
- Глубокие цепочки (author__profile__city) → select_related
Профилировка
Используй django-debug-toolbar или django.db.connection.queries чтобы убедиться что запросов действительно меньше. Иногда prefetch_related с большим IN-списком медленнее чем N+1 для маленьких выборок.