Ответ
Django ORM предоставляет мощный API для взаимодействия с базой данных через объекты QuerySet. Ключевая особенность QuerySet — ленивость (laziness): запрос к БД выполняется только в момент реального обращения к данным.
Методы делятся на две основные группы:
1. Методы, возвращающие новый QuerySet (ленивые)
Эти методы можно выстраивать в цепочки, уточняя запрос.
- `filter(kwargs)
**: Фильтрует записи по заданным условиям (эквивалентWHERE`). - `exclude(kwargs)`**: Исключает записи, соответствующие условиям.
- *`order_by(fields)
**: Сортирует результат (эквивалентORDER BY`). - `annotate(kwargs)
**: Добавляет к каждому объекту вQuerySet` вычисляемое поле (например, с помощью агрегатных функций). - *`values(fields)
**: ВозвращаетQuerySet` словарей вместо объектов модели, что может быть эффективнее. - *`select_related(fields)
**: Оптимизирует запросы, "подтягивая" связанные объекты поForeignKeyилиOneToOneFieldодним SQL-запросом (делаетJOIN`). - *`prefetch_related(lookups)
**: Оптимизирует запросы дляManyToManyFieldи обратныхForeignKey`, выполняя отдельный запрос для связанных объектов и объединяя их в Python.
2. Методы, выполняющие запрос к БД (терминальные)
Эти методы заставляют QuerySet выполнить запрос к базе данных и вернуть результат.
- `get(kwargs)
**: Возвращает один-единственный объект. Вызывает исключениеDoesNotExistилиMultipleObjectsReturned`, если объект не найден или найдено несколько. all(): Возвращает копию текущегоQuerySet(часто используется как начальная точка).count(): Возвращает количество объектов вQuerySet(выполняетSELECT COUNT(*)).first()/last(): Возвращают первый/последний объект после сортировки.exists(): ВозвращаетTrueилиFalse, проверяя, содержит лиQuerySetхотя бы один объект. Эффективнее, чемcount() > 0.
Пример комплексного запроса:
from django.db.models import Count
# Найти 5 самых популярных категорий (с наибольшим количеством активных постов),
# исключая одну конкретную категорию.
top_categories = Category.objects.filter(
post__is_active=True # Фильтруем по полю связанной модели
).exclude(
name='Архив' # Исключаем категорию 'Архив'
).annotate(
num_posts=Count('post') # Добавляем вычисляемое поле с количеством постов
).order_by(
'-num_posts' # Сортируем по убыванию количества постов
)[:5] # Выполняем запрос и берем срез из первых 5 результатов