JavaRush /Курсы /Модуль 3: Django /Примеры сложных фильтраций и аннотаций

Примеры сложных фильтраций и аннотаций

Модуль 3: Django
10 уровень , 7 лекция
Открыта

Сегодня у нас довольно амбициозная цель: соединить все наши знания об аннотациях, фильтрациях и немного "пошаманить" с данными в Django ORM. На практике это означает, что мы будем создавать сложные запросы, которые вы сможете применять в реальной жизни, будь то электронная коммерция, блоги или даже вами созданный "следующий Facebook".

Вот что мы будем делать:

  • Объединять аннотации, фильтрации и агрегации для сложной аналитики.
  • Создавать запросы с использованием логических операторов AND, OR, NOT.
  • Использовать Q-объекты и F-объекты для создания запросов, которые "одна строка кода — тысяча операций".
  • Разбирать реальные примеры из веб-разработки.

Пример 1: самый активный покупатель?

Начнем с простого сценария. У нас есть база данных с заказами Order и пользователями Customer. Мы хотим узнать, кто из пользователей сделал больше всего заказов.

Модели:

from django.db import models

class Customer(models.Model):
    name = models.CharField(max_length=255)
    email = models.EmailField()

    def __str__(self):
        return self.name

class Order(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name="orders")
    total_price = models.DecimalField(max_digits=10, decimal_places=2)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Order #{self.id} by {self.customer.name}"

Запрос с аннотацией и агрегацией:

Сначала сосчитаем количество заказов для каждого пользователя:

from django.db.models import Count

customers_with_order_count = Customer.objects.annotate(order_count=Count('orders')).order_by('-order_count')

for customer in customers_with_order_count:
    print(f"{customer.name} сделал {customer.order_count} заказов.")

Этот запрос генерирует SQL с использованием GROUP BY. В результате мы получили список, где у каждого пользователя есть поле order_count, показывающее количество его заказов.

Но ведь нам нужен только самый активный покупатель! Да не проблема:

most_active_customer = Customer.objects.annotate(order_count=Count('orders')).order_by('-order_count').first()
print(f"Самый активный пользователь: {most_active_customer.name}, он сделал {most_active_customer.order_count} заказов.")

Пример 2: отобразить заказы с высоким чеком

Теперь предположим, что нам нужно выбрать заказы, в которых сумма чека выше среднего по всем заказам.

Рассчитаем среднюю сумму заказа:

from django.db.models import Avg

average_total_price = Order.objects.aggregate(avg_price=Avg('total_price'))['avg_price']
print(f"Средний чек по всем заказам: {average_total_price}")

А теперь выберем заказы с чеком выше среднего:

high_value_orders = Order.objects.filter(total_price__gt=average_total_price)

for order in high_value_orders:
    print(f"Order #{order.id}, сумма: {order.total_price}")

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

Пример 3: объединение фильтраций с использованием Q-объектов

Теперь представьте, что у нас есть следующий сценарий: мы хотим выбрать заказы, которые были либо сделаны определенным пользователем, либо имеют сумму выше 1000.

Решение с Q-объектами:

from django.db.models import Q

user_id = 1  # ID пользователя, которого мы ищем
high_value_orders = Order.objects.filter(Q(customer_id=user_id) | Q(total_price__gt=1000))

for order in high_value_orders:
    print(f"Order #{order.id}, сумма: {order.total_price}")

В этом запросе мы комбинируем два условия с использованием оператора | (логическое OR). Вы также можете использовать & (логическое AND) и ~ (логическое NOT) для создания более сложных условий.

Пример 4: сравнение полей одной модели (F-объекты)

Давайте теперь попробуем немного поэкспериментировать. Представим, что у нас есть таблица "скидок" (Discount) с полями original_price и discounted_price. Мы хотим найти все скидки, где скидка превышает 20%.

Модель:

class Discount(models.Model):
    original_price = models.DecimalField(max_digits=10, decimal_places=2)
    discounted_price = models.DecimalField(max_digits=10, decimal_places=2)

Фильтрация с использованием F-объектов:

from django.db.models import F

big_discounts = Discount.objects.filter(
    original_price__gt=F('discounted_price') * 1.25
)

for discount in big_discounts:
    print(f"Original: {discount.original_price}, Discounted: {discount.discounted_price}")

С помощью F-объектов мы можем сравнивать значения полей внутри одной записи без необходимости загружать их в Python. Элегантно, не так ли?

Пример 5: Комбинированные запросы с аннотациями и агрегациями

Теперь давайте усложним задачу. Мы хотим получить всех пользователей и для каждого из них вычислить:

  1. Количество заказов.
  2. Суммарную стоимость всех их заказов.

Но выводить мы будем только тех пользователей, у которых суммарная стоимость заказов превышает 5000.

Запрос:

from django.db.models import Sum

rich_customers = Customer.objects.annotate(
    total_spent=Sum('orders__total_price'),
    order_count=Count('orders')
).filter(total_spent__gt=5000).order_by('-total_spent')

for customer in rich_customers:
    print(f"{customer.name} потратил {customer.total_spent} в {customer.order_count} заказах.")

Мы комбинируем аннотации Sum и Count, фильтрацию filter(total_spent__gt=5000) и сортировку order_by('-total_spent'). Django ORM прямо-таки блестяще справляется с такими задачами.

Пример 6: группировка и фильтрация сложных данных

Наконец, представьте, что нам нужно составить отчет только по пользователям, которые сделали больше 5 заказов и потратили в сумме более 10 000.

Запрос:

rich_and_active_customers = Customer.objects.annotate(
    total_spent=Sum('orders__total_price'),
    order_count=Count('orders')
).filter(
    total_spent__gt=10000,
    order_count__gt=5
)

for customer in rich_and_active_customers:
    print(f"{customer.name} сделал {customer.order_count} заказов и потратил {customer.total_spent}.")

Этот запрос показывает, как можно объединять аннотации с множественными фильтрами, чтобы получить детализированные результаты.

Практическая ценность

В реальной жизни такие запросы находят применение в самых разнообразных задачах:

  • Отчеты для менеджеров о поведении пользователей.
  • Аудит данных для маркетинговых кампаний.
  • Оптимизация рекомендаций и аналитика продаж.

Ваш следующий собеседник будет впечатлен, когда вы расскажете, что умеете писать "умные" запросы с использованием аннотаций, фильтраций, Q- и F-объектов. Бонусом к этому идет еще и ваша способность разбираться в производительности запросов.

1
Задача
Модуль 3: Django, 10 уровень, 7 лекция
Недоступна
Фильтрация по возрастной группе
Фильтрация по возрастной группе
1
Задача
Модуль 3: Django, 10 уровень, 7 лекция
Недоступна
Анализ активности по времени
Анализ активности по времени
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Иван Уровень 73
8 сентября 2025

    prods = Product.objects.filter(purchases__customer__age__range=(18, 25)
    ).annotate(total_purchases=Count('purchases__id')
    ).order_by('-total_purchases').values('name', 'total_purchases')
Прошу прощения...А что тут не правильно? Задача 15