← Назад к вопросам

В чем разница между annotate и aggregate в Django?

2.0 Middle🔥 191 комментариев
#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Различия между annotate и aggregate в Django

Это две разные операции QuerySet, которые часто путают разработчики. Хотя обе выполняют агрегацию, они работают по-разному и возвращают разные результаты.

aggregate — глобальная агрегация

Назначение: aggregate вычисляет ОДНО значение для ВСЕГО QuerySet. Он возвращает словарь с результатом.

from django.db.models import Count, Sum, Avg, Max, Min
from myapp.models import Order

# Получить общее количество заказов
result = Order.objects.aggregate(total_orders=Count("id"))
print(result)  # {total_orders: 150}

# Несколько агрегаций одновременно
stats = Order.objects.aggregate(
    total_orders=Count("id"),
    total_revenue=Sum("amount"),
    average_order=Avg("amount"),
    max_order=Max("amount"),
    min_order=Min("amount")
)
print(stats)
# {
#     "total_orders": 150,
#     "total_revenue": 50000.00,
#     "average_order": 333.33,
#     "max_order": 5000.00,
#     "min_order": 10.00
# }

Ключевые моменты:

  • Возвращает один словарь с агрегированными значениями
  • Применяется к ВСЕМУ QuerySet
  • Результат НЕ привязан к отдельным объектам
  • Терминирует QuerySet (не может быть дальше в цепочке)

annotate — агрегация для каждого объекта

Назначение: annotate добавляет вычисленное поле к КАЖДОМУ объекту в QuerySet. Он сохраняет QuerySet, позволяя дальнейшие операции.

from django.db.models import Count, Sum, Avg
from myapp.models import Author

# Добавить количество книг для каждого автора
authors = Author.objects.annotate(
    book_count=Count("books")
)

for author in authors:
    print(f"{author.name}: {author.book_count} books")
    # Объект author теперь имеет атрибут book_count

# Более сложный пример: средний рейтинг для каждого автора
authors_with_rating = Author.objects.annotate(
    avg_rating=Avg("books__rating"),
    total_sales=Sum("books__sales")
)

for author in authors_with_rating:
    print(f"{author.name}: Avg Rating={author.avg_rating}, Sales={author.total_sales}")

Ключевые моменты:

  • Возвращает QuerySet с объектами, каждый с дополнительными полями
  • Каждый объект получает своё вычисленное значение
  • QuerySet остаётся "живым" (можно использовать filter, order_by и т.д.)
  • Можно цепить дальшейшие операции

Практическое сравнение

# Модели
class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name="books")
    title = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=10, decimal_places=2)

# ===== aggregate =====
# Вопрос: Сколько всего книг и какой общий доход?
result = Book.objects.aggregate(
    total_books=Count("id"),
    total_revenue=Sum("price")
)
print(result)  # {total_books: 500, total_revenue: 50000.00}

# ===== annotate =====
# Вопрос: Для каждого автора — сколько у него книг и какой доход?
authors = Author.objects.annotate(
    book_count=Count("books"),
    author_revenue=Sum("books__price")
)

for author in authors:
    print(f"{author.name}: {author.book_count} books, Revenue: {author.author_revenue}")

Комбинирование annotate + aggregate

Можно использовать их вместе! Сначала annotate для каждого объекта, потом aggregate для результата:

# Найти среднее количество книг на автора
result = Author.objects.annotate(
    book_count=Count("books")
).aggregate(
    avg_books_per_author=Avg("book_count")
)

print(result)  # {avg_books_per_author: 12.5}

Сравнительная таблица

Параметрaggregateannotate
ВозвращаетОдин словарьQuerySet с объектами
Область примененияВесь QuerySetКаждый объект в QuerySet
ЦепируемостьНет (терминирует)Да (продолжает цепь)
ИспользованиеОбщая статистикаСтатистика по группам/объектам
SQLGROUP BY всёGROUP BY по first()

Типичные use cases

aggregate:

# Общее количество пользователей
User.objects.aggregate(total=Count("id"))

# Общая сумма доходов
Order.objects.aggregate(revenue=Sum("amount"))

# Средняя оценка всех продуктов
Product.objects.aggregate(avg_rating=Avg("rating"))

annotate:

# Рейтинг для каждого ресторана
Restaurant.objects.annotate(avg_rating=Avg("reviews__rating"))

# Количество подписчиков для каждого блоггера
Blogger.objects.annotate(follower_count=Count("followers"))

# Топ авторов по количеству статей
Author.objects.annotate(article_count=Count("articles")).order_by("-article_count")[:10]

Вывод

aggregate — когда нужна одна цифра для всех данных ("Сколько всего?")

annotate — когда нужны значения для каждого объекта ("Для каждого... сколько?")

В чем разница между annotate и aggregate в Django? | PrepBro