TGViewer
@davidobryakov @davidobryakov @davidobryakov · 638 subscribers
Post #1195 664
👩‍💻 Оптимизация запросов в Django ORM

#python #django #backend

Сегодня хочу рассказать про методы select_related и prefetch_related: показать разницу между ними, привести примеры использования. Полная версия с примерами доступна у меня в блоге.

В чём разница?

Если говорить совсем кратко, то: select_related — это JOIN, то есть происходит 1 запрос, в рамках которого связываются указанные сущности и приходят в python уже связанными, в то время, как prefetch_related — делает отдельные запросы по каждой указанной связанной сущности и производит связывание на уровне python.

В документации django сказано, что select_related стоит применять в тех случаях, когда вы используете ForeignKey, если вы используете ManyToManyField, ваш выбор — это prefetch_related.

Подробности и подводные камни

Предположим, что наши модели выглядят следующим образом:

from django.db import models

class Genre(models.Model):
name = models.CharField(max_length=512)

class City(models.Model):
...

class Person(models.Model):
hometown = models.ForeignKey(
City,
)

class Book(models.Model):
name = models.CharField(max_length=512)

author = models.ForeignKey(Person, on_delete=models.CASCADE)

genres = models.ManyToManyField(Genre)


Инвалидация кэша

⚠️ При использовании prefetch_related, важно помнить о такой вещи, как инвалидация кэша на уровне Queryset (инвалидация может быть вызвана любым дополнительным методом в цепочке, который подразумевает другой запрос к базе данных). Например, при использовании фильтрации в следующем примере, будут выполнены отдельные запросы для каждого объекта Book:

for book in Book.objects.prefetch_related("genres"):
print(book.name, ":")
for genre in Book.genres.filter(age_rating="18+"):
print(" ", genre.name)


Объект Prefetch

Также, стоит упомянуть и о существовании объекта Prefetch, который позволяет использовать подготовленный Queryset вместе с prefetch_related. Например, мы хотим отобрать заранее только те жанры, которые имеют возрастной рейтинг "18+" и посчитать количество книг по каждому такому жанру.

Код для решения этой задачи выглядит следующим образом:

genres = Genre.objects.filter(age_rating="18+")
.annotate(books_count=Count("book_set"))

queryset = Book.objects.all().prefetch_related(
Prefetch("genres", queryset=genres)
)


Полная версия с примерами доступна у меня в блоге.

📖 Источники:

1) Статья с хабра, в которой всё доходчиво объясняют: https://habr.com/ru/articles/752574/
2) Документация Django: https://docs.djangoproject.com/en/5.0/ref/models/querysets/
Teletype Оптимизация запросов в Django ORM Сегодня хочу рассказать про методы select_related и prefetch_related: показать разницу между ними, привести примеры использования.
  • 👍 7
More from @davidobryakov
  1. Feb 14, 2026Оптимизация работы эндпоинта Часто так бывает, что новые фичи заводятся итерационным путём…
  2. May 24, 2025Настраиваем автодокументирование для express-приложений Читать полностью: https://blog.kan…
  3. May 13, 2025Основные паттерны микросервисной архитектуры: Strangler Fig, API Gateway, Service Mesh и д…
  4. May 11, 2025Поиск мотивации в скучных задачах Частенько бывает, что нехватка мотивации для решения чег…
  5. May 10, 2025Переход с Python на Go и мысли о высшем образовании / ч. 3 Пост в блоге: https://blog.kant…
  6. May 10, 2025Переход с Python на Go и мысли о высшем образовании / ч. 2 Пост в блоге: https://blog.kant…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →