Select и prefetch related: работа с БД для конкретной логики
В Django ORM есть много способов отойти от стандартных запросов, которые генерируются при запросах вида Book.objects.filter(category_id=category_id), и сделать работу с БД оптимальной для конкретной логики.
Среди подобных методов отдельное место занимают select_related и prefetch_related. Во-первых, в некоторых случаях они позволяют десятком символов снизить нагрузку на БД на порядок. Во-вторых, при неправильном их использовании можно убить приложение. А в-третьих, на собеседованиях на позицию начинающего веб-разработчика почти никто не может рассказать о разнице между ними.
Рассмотрим пример
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()])
Такой код сгенерирует четыре запроса: вытащит книгу, потом категорию, потом авторов, чтобы вывести их имена, потом опять авторов, чтобы вывести их номера. Было бы классно снизить количество запросов: авторов вытаскивать один раз, а категорию вытаскивать одним запросом с книгой.
Давайте начнём с категории
Тут нам пригодится select_related:
Book.objects.select_related(‘category’).get(pk=book_id)
за один запрос вытащит и книгу и категорию и тогда
Круто: мы сэкономили один запрос и не получили никаких штрафов. Хочется проделать нечто подобное с авторами, но увы: select_related(‘authors’) сделать не получится.
Всё дело в том, что тут отношение «многие ко многим», а в этом случае нельзя просто взять, сделать join и за один запрос вытащить и книгу, и всех авторов.
Тут-то и пригодится prefetch_related
Вот так:
Book.objects.prefetch_related(‘authors’).get(pk=book_id)
Здесь произойдёт два запроса: один вытащит книгу, а другой – всех авторов. Объединит их уже ORM на стороне Python. Теперь
print([a.full_name for a in book.authors.all()])
не сгенерирует ни одного запроса: всё уже есть.
Как видите, select_related и prefetch_related – мощные команды, которые работают совсем по-разному. Используйте их к месту, радуйте своих DBA и спите спокойно!
Есть вопрос? Напишите в комментариях!