Ответ
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-запросов.
Ответ 18+ 🔞
Слушай, а вот этот твой Django ORM — это ж, блядь, просто песня, а не инструмент! Сидишь себе, попиваешь кофеёк, а он там в недрах базы данных такие пируэты выписывает, что мама не горюй. И главное — SQL руками почти не пишешь, ёпта!
Вот смотри, какие у него фишки есть, просто овердохуища:
annotate()иaggregate(): Это как два брата-близнеца, но один — зануда, а второй — душа компании.aggregate()— тот самый зануда: посчитает что-то по всей таблице (сумму, среднее, хуй с горы) и выдаст один сухой словарик. Аannotate()— весёлый парень: к каждой записи в результатах прилепит вычисленное поле. Хочешь к каждому автору количество книг прикрутить? Легко!F()-объекты: Вообще гениальная хуйня. Позволяет в запросе одно поле модели с другим сравнивать, прямо в базе, без вытаскивания всего на свете в память. Типа: «найди всех, у кого количество просмотров больше количества лайков». База сама всё посчитает, красота!Q()-объекты: А это, сука, для настоящих гурманов логики. Когда простогоfilter()уже мало и хочется склеить условия через «ИЛИ», «И», «НЕ». Чувствуешь себя повелителем булевой алгебры, блядь!select_related()иprefetch_related(): Вот это, блядь, святое! Без них твоё приложение сожрёт базу запросами как мартышка банан.select_related()— для связей «один к одному» или «ко многим от одного» (foreign key), делает JOIN и тащит всё за раз.prefetch_related()— для «многие ко многим», хитро предзагружает связки отдельными запросами. Пользоваться — обязательно, иначе N+1 проблему получишь в полный рост, пиздец.Subqueryи оконные функции: Ну это уже для циркачей высшей категории. Когда нужно внутри запроса ещё один запрос встроить или ранжирование какое-то сделать (типа «присвоить порядковый номер записям в группе»). Мощно, но голова может болеть.
Вот, смотри, пример, чтобы не быть голословным, как annotate с Subquery дружат:
Допустим, есть автор и книги. Надо авторов вывести, и рядом — сколько книг у каждого. Проще простого!
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')) считаем книжки
book_count_subquery = Book.objects.filter(
author=OuterRef('pk')
).values('author').annotate(
total_books=Count('id')
).values('total_books')[: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 упрётся в потолок и не сможет какую-то дичь вытворять (бывает, чёрт побери!), то всегда есть сырой SQL. Через raw() или connection.cursor(). Но это уже как ножевой бой — мощно, но можно и по пальцам порезаться, если не умеешь.