Если вы читаете эту статью, скорее всего, вы сталкивались с проблемой N+1 во время разработки на Django или хоть раз с ней сталкивались. Из-за этой проблемы скорость загрузки страниц снижается, и вы, вероятно, узнали о select_related и prefetch_related в поисках решения.

Вы нашли мой блог! 🎉 Прочитав эту статью до конца, вы точно освоите эффективные способы решения проблемы N+1.

Если вы еще не совсем понимаете концепцию проблемы N+1, я рекомендую сначала прочитать статью ниже.

Что такое проблема N+1 в Django ORM? Почему она возникает?

🔍 Два способа решения проблемы N+1 в Django

В Django ORM есть select_related и prefetch_related, чтобы решить проблему N+1. Однако важно точно понимать, при каких обстоятельствах следует использовать каждую из этих функций, поскольку они работают по-разному.

📌 Общий принцип select_related и prefetch_related

Главный общий принцип этих двух методов состоит в том, что они извлекают необходимые поля из базы данных в первом запросе, чтобы эффективно использовать извлеченные данные внутри Django.

Например, представьте, что вы идете в крупный супермаркет, чтобы испечь торт, и покупаете все необходимые ингредиенты, такие как мука, сливки и фрукты, сразу. Когда вы вернетесь домой и начнете готовить, у вас уже будет готовый набор всех ингредиентов, и вы сможете сразу приступить к приготовлению.

Но что, если вы будете покупать необходимые ингредиенты по одному? Вы купили муку и вернулись, чтобы начать замешивать тесто, но обнаружили, что у вас нет сливок, и вам снова надо идти в супермаркет. Затем вы купили сливки и пытаетесь продолжить работу, но теперь у вас нет фруктов, и вам снова нужно идти в магазин. Что тогда?

В таком формате приготовление займет очень много времени. select_related и prefetch_related в Django ORM помогают избежать такого неэффективного извлечения данных, извлекая необходимые данные за один запрос к базе данных, чтобы быстро их обработать.

Select Related vs Prefetch Related in Django ORM

✅ 1. select_related – немедленная загрузка с использованием SQL JOIN

Используется в отношениях ForeignKey(1:N)

  • Получает данные одним запросом с помощью SQL JOIN
  • Так как связанные объекты загружаются сразу, дополнительных запросов к базе данных нет

📌 Пример использования select_related

authors = Author.objects.select_related('post_set').all()

🧐 SQL, выполняемая, когда select_related не применяется

SELECT * FROM author;
SELECT * FROM post WHERE author_id = 1;
SELECT * FROM post WHERE author_id = 2;
...

🚀 SQL, выполняемая, когда применяется select_related

SELECT * FROM author INNER JOIN post ON author.id = post.author_id;

✅ 2. prefetch_related – предварительная загрузка с использованием отдельных запросов

Используется в отношениях ManyToMany, Reverse ForeignKey

  • Выполняет отдельные запросы, затем Django оптимизирует данные на Python и соединяет их
  • Так как SQL JOIN не используется, это может быть более выгодно при обработке больших объемов данных

📌 Пример использования prefetch_related

authors = Author.objects.prefetch_related('post_set').all()

🧐 SQL, выполняемая после применения prefetch_related

SELECT * FROM author;
SELECT * FROM post WHERE author_id IN (1, 2, 3, 4, 5, ...);

🎯 Заключение – вот и всё, чтобы решить проблему N+1!

Если не решить проблему N+1 при использовании Django ORM, это может серьёзно ухудшить производительность. Но, используя select_related и prefetch_related правильно, вы сможете минимизировать количество выполненных SQL-запросов и значительно улучшить скорость загрузки страниц.

✅ Резюме

  • Если это отношение ForeignKey(1:N), используйте select_related
  • Если это отношение ManyToMany или обратное отношение, используйте prefetch_related
  • В любом случае необходимо проверить выполняемые SQL-запросы и оптимизировать их
  • Если скорость выполнения запросов снижается, следует подозревать проблему N+1

📌 Прочитайте также статьи на эту тему!

Что такое проблема N+1 в Django ORM? Почему она возникает?

Теперь попробуйте решить проблему N+1 с помощью select_related и prefetch_related! 🚀 Если что-то не совсем понятно или у вас есть дополнительные вопросы, оставьте комментарий или задайте вопрос. 😊