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.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-об'єктів. Бонусом до цього йде ще й ваша здатність розбиратися у продуктивності запитів.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ