Ответ
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-запросов.