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

Ответ

В 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.

Ответ 18+ 🔞

О, слушай, а вот эта ваша Django ORM, она же, блядь, как хитрая жопа — вроде всё просто, а потом бац, и ты уже в дебрях. Ну, например, тебе надо сгруппировать данные, как в SQL GROUP BY. Так вот, забудь про сырой SQL, тут свои заморочки есть.

Вот смотри, чтобы сгруппировать, тебе надо два метода в паре использовать: .values() и .annotate(). Это как, блядь, танец такой — один ведёт, другой поддакивает.

  • **.values('поле_группировки')** — это ты говоришь ORM: «Слушай, группируй вот по этому полю, ясно?». SQL-аналог — тот самый GROUP BY field_name.
  • **.annotate(название_агрегата=АгрегатнаяФункция('поле'))** — а это уже: «А теперь для каждой полученной группы посчитай мне что-нибудь, например, сколько там записей или сумму нарисуй». Это типа COUNT(*) или SUM(price) в SQL.

Вот важный момент, ёпта! Порядок — святое. Сначала всегда .values(), а потом уже .annotate(). Если наоборот сделаешь — получишь пиздец и не тот результат, а потом будешь голову ломать, что пошло не так.

Ну, пример, чтобы совсем понятно стало

Допустим, есть у тебя модель Product (Продукт, ну). И ты хочешь узнать, сколько продуктов в каждой категории валяется. Задача-то проще пареной репы.

Как бы это в SQL выглядело:

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

А вот как это на Django ORM делается, без всякого там сырого SQL:

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 вернёт:
# <QuerySet [{'category': 'Электроника', 'product_count': 150}, {'category': 'Книги', 'product_count': 89}]>

# Ну и дальше выводишь, как душе угодно
for item in products_per_category:
    print(f"Категория: {item['category']}, Количество: {item['product_count']}")
    # Выведет что-то вроде:
    # Категория: Электроника, Количество: 150
    # Категория: Книги, Количество: 89

Вот и вся магия, блядь. Сидишь себе, методы эти вызываешь, и ORM сама всю грязную работу делает — никаких тебе ручных GROUP BY писать не надо. Удобно же, в рот меня чих-пых! Главное — порядок не перепутай, а то будет волнение ебать, где ошибка.