#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/