Ответ
Оптимизация запросов в 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()
.