Ответ
Оптимизация запросов в Django ORM направлена на сокращение количества обращений к базе данных и уменьшение объема передаваемых данных. Это достигается в основном за счет решения проблемы "N+1 запроса".
Ключевые методы:
-
*`select_related(fields)
** Используется для связейForeignKeyиOneToOneField. Он "расширяет" основной запрос, добавляя связанные объекты черезSQL JOIN`. Это позволяет избежать отдельных запросов к связанным таблицам при доступе к ним.# Плохо: N+1 запросов (1 для постов, N для авторов) posts = Post.objects.all() for post in posts: print(post.author.name) # Хорошо: 1 запрос с JOIN posts = Post.objects.select_related('author').all() for post in posts: print(post.author.name) -
*`prefetch_related(lookups)
** Применяется для связейManyToManyFieldи обратныхForeignKey. В отличие отselect_related, он выполняет отдельный запрос для связанных объектов и "соединяет" их на уровне Python. Это эффективнее, чемJOIN` для связей "многие-ко-многим".# Плохо: N+1 запросов (1 для блогов, N для постов в каждом блоге) blogs = Blog.objects.all() for blog in blogs: for post in blog.posts.all(): print(post.title) # Хорошо: 2 запроса (1 для блогов, 1 для всех связанных постов) blogs = Blog.objects.prefetch_related('posts').all() for blog in blogs: for post in blog.posts.all(): # Данные уже загружены print(post.title) -
only(*fields)иdefer(*fields)Позволяют контролировать, какие поля модели загружать из базы данных.only()загружает только указанные поля, аdefer()загружает все, кроме указанных. Это сокращает трафик и потребление памяти.# Загрузить только имена и email пользователей users = User.objects.only('username', 'email') -
exists()иcount()Для проверки существования хотя бы одного объектаexists()значительно эффективнее, чемcount() > 0.exists()транслируется вSQL EXISTS, который прекращает поиск после первого найденного совпадения, в то время какcount()всегда сканирует всю таблицу.# Хорошо: быстрый запрос EXISTS if Post.objects.filter(is_published=True).exists(): print("Есть опубликованные посты.") -
bulk_create()иbulk_update()Для массового создания или обновления объектов эти методы объединяют операции в один SQL-запрос, что на порядки быстрее, чем выполнение запросов в цикле. -
Индексы в моделях Добавление индексов для полей, по которым часто происходит фильтрация, ускоряет операции
filter(),exclude()иget()на уровне базы данных.class Post(models.Model): title = models.CharField(max_length=200) publish_date = models.DateTimeField() class Meta: indexes = [ models.Index(fields=['publish_date']), ]
Для анализа и отладки запросов рекомендуется использовать django-debug-toolbar или QuerySet.explain().