Что такое аннотации в Django ORM?
Что проверяют
Умеете ли вы считать на стороне базы, а не в цикле по объектам. Вопрос отделяет тех, кто собирал отчёты на ORM, от тех, кто знает только фильтры и выборки.
Как отвечать
annotate добавляет к каждой строке набора вычисляемое поле, которое считает база. Это не только агрегаты: туда же уходят F, Value, Concat, ветвления через Case и When, оконные функции и подзапросы.
authors = (
Author.objects
.annotate(book_count=Count("books", distinct=True))
.filter(book_count__gt=2)
)Разграничьте соседние инструменты. aggregate сворачивает весь набор в один словарь, annotate считает построчно. alias вычисляет выражение для фильтрации, но не тянет его в выборку. Порядок важен: фильтр до annotate сужает строки, которые попадут в агрегат, а фильтр после annotate превращается в условие по посчитанному значению.
Стоит упомянуть, что values перед annotate задаёт группировку: в группировку попадёт ровно то, что перечислено. И что агрегат по связи «один ко многим» строится на левом соединении, поэтому сумма и среднее возвращают пустое значение вместо нуля там, где связанных строк нет, а лечится это функцией Coalesce.
Где спотыкаются
Ставят два агрегата по разным связям в один вызов и получают завышенные числа: соединения перемножают строки. Спасает distinct=True там, где считаем количество, и подзапрос через Subquery там, где нужна сумма.
Забывают про сортировку по умолчанию. Если у модели задан порядок в Meta, поля сортировки уезжают в группировку, и она разваливается на отдельные строки. Перед annotate в таком случае ставят пустой order_by.
Считают агрегат в Python: тянут все объекты и складывают в цикле. На сотне записей это незаметно, на боевой таблице превращается в перебор всей таблицы и лавину запросов к связанным объектам.