0 added
0 removed
Original
2026-01-01
Modified
2026-03-10
1
<p>Теги: python, web-разработка, web, web development, prefetch_related, select_related, select_related(‘authors’), join, django orm, для собеседования</p>
1
<p>Теги: python, web-разработка, web, web development, prefetch_related, select_related, select_related(‘authors’), join, django orm, для собеседования</p>
2
<p>В<strong>Django ORM</strong>есть много способов отойти от стандартных запросов, которые генерируются при запросах вида<strong>Book.objects.filter(category_id=category_id)</strong>, и сделать работу с БД оптимальной для конкретной логики.</p>
2
<p>В<strong>Django ORM</strong>есть много способов отойти от стандартных запросов, которые генерируются при запросах вида<strong>Book.objects.filter(category_id=category_id)</strong>, и сделать работу с БД оптимальной для конкретной логики.</p>
3
<p>Среди подобных методов отдельное место занимают<strong>select_related</strong>и<strong>prefetch_related</strong>. Во-первых, в некоторых случаях они позволяют десятком символов снизить нагрузку на БД на порядок. Во-вторых, при неправильном их использовании можно убить приложение. А в-третьих, на собеседованиях на позицию начинающего веб-разработчика почти никто не может рассказать о разнице между ними.</p>
3
<p>Среди подобных методов отдельное место занимают<strong>select_related</strong>и<strong>prefetch_related</strong>. Во-первых, в некоторых случаях они позволяют десятком символов снизить нагрузку на БД на порядок. Во-вторых, при неправильном их использовании можно убить приложение. А в-третьих, на собеседованиях на позицию начинающего веб-разработчика почти никто не может рассказать о разнице между ними.</p>
4
<h2>Рассмотрим пример</h2>
4
<h2>Рассмотрим пример</h2>
5
class Book(Model): category = ForeignKey(Category) authors = ManyToManyField(Author) … book = Book.objects.get(pk=book_id) print(book.category.name) print([a.full_name for a in book.authors.all()]) print([a.pk for a in book.authors.all()])<p>Такой код сгенерирует четыре запроса: вытащит книгу, потом категорию, потом авторов, чтобы вывести их имена, потом опять авторов, чтобы вывести их номера. Было бы классно снизить количество запросов: авторов вытаскивать один раз, а категорию вытаскивать одним запросом с книгой.</p>
5
class Book(Model): category = ForeignKey(Category) authors = ManyToManyField(Author) … book = Book.objects.get(pk=book_id) print(book.category.name) print([a.full_name for a in book.authors.all()]) print([a.pk for a in book.authors.all()])<p>Такой код сгенерирует четыре запроса: вытащит книгу, потом категорию, потом авторов, чтобы вывести их имена, потом опять авторов, чтобы вывести их номера. Было бы классно снизить количество запросов: авторов вытаскивать один раз, а категорию вытаскивать одним запросом с книгой.</p>
6
<h2>Давайте начнём с категории</h2>
6
<h2>Давайте начнём с категории</h2>
7
<p>Тут нам пригодится<strong>select_related</strong>:</p>
7
<p>Тут нам пригодится<strong>select_related</strong>:</p>
8
Book.objects.select_related(‘category’).get(pk=book_id)<p>за один запрос вытащит и книгу и категорию и тогда book.category.name уже не сгенерирует запроса.</p>
8
Book.objects.select_related(‘category’).get(pk=book_id)<p>за один запрос вытащит и книгу и категорию и тогда book.category.name уже не сгенерирует запроса.</p>
9
<p>Круто: мы сэкономили один запрос и не получили никаких штрафов. Хочется проделать нечто подобное с авторами, но увы:<strong>select_related(‘authors’)</strong>сделать не получится.</p>
9
<p>Круто: мы сэкономили один запрос и не получили никаких штрафов. Хочется проделать нечто подобное с авторами, но увы:<strong>select_related(‘authors’)</strong>сделать не получится.</p>
10
<p>Всё дело в том, что тут отношение "многие ко многим", а в этом случае нельзя просто взять, сделать<strong>join</strong>и за один запрос вытащить и книгу, и всех авторов.</p>
10
<p>Всё дело в том, что тут отношение "многие ко многим", а в этом случае нельзя просто взять, сделать<strong>join</strong>и за один запрос вытащить и книгу, и всех авторов.</p>
11
<h2>Тут-то и пригодится prefetch_related</h2>
11
<h2>Тут-то и пригодится prefetch_related</h2>
12
<p>Вот так:</p>
12
<p>Вот так:</p>
13
Book.objects.prefetch_related(‘authors’).get(pk=book_id)<p>Здесь произойдёт два запроса: один вытащит книгу, а другой - всех авторов. Объединит их уже ORM на стороне Python. Теперь</p>
13
Book.objects.prefetch_related(‘authors’).get(pk=book_id)<p>Здесь произойдёт два запроса: один вытащит книгу, а другой - всех авторов. Объединит их уже ORM на стороне Python. Теперь</p>
14
print([a.full_name for a in book.authors.all()])<p>не сгенерирует ни одного запроса: всё уже есть.</p>
14
print([a.full_name for a in book.authors.all()])<p>не сгенерирует ни одного запроса: всё уже есть.</p>
15
<p>Как видите,<strong>select_related</strong>и<strong>prefetch_related</strong>- мощные команды, которые работают совсем по-разному. Используйте их к месту, радуйте своих DBA и спите спокойно!</p>
15
<p>Как видите,<strong>select_related</strong>и<strong>prefetch_related</strong>- мощные команды, которые работают совсем по-разному. Используйте их к месту, радуйте своих DBA и спите спокойно!</p>
16
<p><em>Есть вопрос? Напишите в комментариях!</em></p>
16
<p><em>Есть вопрос? Напишите в комментариях!</em></p>
17
17