Ответ
Django ORM предоставляет мощный набор инструментов для построения сложных запросов, позволяя эффективно работать с базой данных без написания чистого SQL.
Основные инструменты:
annotate()
иaggregate()
: Используются для выполнения агрегирующих функций (например,Count
,Sum
,Avg
) и добавления вычисляемых полей к каждому объекту в QuerySet.aggregate()
возвращает словарь с агрегированными значениями для всего QuerySet, аannotate()
добавляет аннотированное поле к каждому объекту.F()
-объекты: Позволяют ссылаться на поля модели в запросах, что полезно для сравнения двух полей одной модели или выполнения арифметических операций над ними непосредственно в базе данных.Q()
-объекты: Используются для построения сложных логических условий (AND
,OR
,NOT
) в запросах, объединяя несколько условий фильтрации. Это позволяет создавать более гибкие и читаемые условия.select_related()
иprefetch_related()
: Методы для оптимизации запросов к связанным моделям и решения проблемы N+1 запросов.select_related()
выполняет SQL JOIN и загружает связанные объекты в одном запросе (для отношений "один-ко-многим" и "один-к-одному"), аprefetch_related()
выполняет отдельные запросы для каждой связанной модели и объединяет их в Python (для отношений "многие-ко-многим" и "обратных" отношений).Subquery
иWindow functions
: Для выполнения подзапросов и использования оконных функций базы данных (например,ROW_NUMBER()
,RANK()
) для более сложной аналитики и ранжирования данных.
Пример сложного запроса с annotate
и Subquery
:
Предположим, у нас есть модели Author
и Book
, и мы хотим получить всех авторов с количеством их книг.
from django.db import models
from django.db.models import Count, Subquery, OuterRef
# Пример моделей (для контекста)
class Author(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
def __str__(self):
return self.title
# Подзапрос для подсчета книг каждого автора
# OuterRef('pk') ссылается на pk текущего автора из внешнего запроса
book_count_subquery = Book.objects.filter(
author=OuterRef('pk')
).values('author').annotate(
total_books=Count('id')
).values('total_books')[:1] # [:1] для получения одного значения из подзапроса
# Основной запрос: аннотируем авторов количеством их книг
authors_with_book_counts = Author.objects.annotate(
num_books=Subquery(book_count_subquery, output_field=models.IntegerField())
)
for author in authors_with_book_counts:
print(f"Автор: {author.name}, Книг: {author.num_books if author.num_books is not None else 0}")
В случаях, когда ORM не предоставляет достаточной гибкости или производительности, можно использовать raw SQL через методы raw()
для получения QuerySet-подобных объектов или connection.cursor()
для выполнения произвольных SQL-запросов.