Как в Django ORM выполнить запрос с группировкой (GROUP BY)?

«Как в Django ORM выполнить запрос с группировкой (GROUP BY)?» — вопрос из категории Django, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Django ORM группировка данных, аналогичная SQL-оператору GROUP BY, выполняется с помощью комбинации методов QuerySet: .values() и .annotate().

  • **.values('field_name', ...)**: Указывает, по каким полям нужно сгруппировать данные. SQL-аналог: GROUP BY field_name.
  • **.annotate(agg_name=AggregationFunction('field'))**: Добавляет к каждому объекту в QuerySet вычисленное значение (агрегацию), например, количество или сумму. SQL-аналог: COUNT(*), SUM(price).

Важно: Порядок вызова методов имеет значение. Сначала .values() для указания полей группировки, а затем .annotate() для агрегации.

Пример

Допустим, у нас есть модель Product и мы хотим посчитать количество продуктов в каждой категории.

SQL-эквивалент:

SELECT category, COUNT(id) as product_count
FROM products_product
GROUP BY category;

Реализация на Django ORM:

from django.db.models import Count
from myapp.models import Product

# Группируем по полю 'category' и для каждой группы считаем количество записей
products_per_category = Product.objects 
    .values('category') 
    .annotate(product_count=Count('id')) 
    .order_by('-product_count') # Опционально: сортируем по убыванию количества

# Результат будет выглядеть примерно так:
# <QuerySet [{'category': 'Электроника', 'product_count': 150}, {'category': 'Книги', 'product_count': 89}]>

for item in products_per_category:
    print(f"Категория: {item['category']}, Количество: {item['product_count']}")

Этот подход позволяет выполнять сложные агрегирующие запросы, оставаясь в рамках Django ORM, без написания сырого SQL.