Сьогодні у нас досить амбітна мета: поєднати всі наші знання про анотації, фільтрації та трохи "пошаманити" з даними в 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: Комбіновані запити з анотаціями та агрегаціями
Тепер давайте ускладнимо задачу. Ми хочемо отримати всіх користувачів і для кожного з них обчислити:
- Кількість замовлень.
- Сумарну вартість всіх їхніх замовлень.
Але виводити ми будемо тільки тих користувачів, у яких сумарна вартість замовлень перевищує 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-об'єктів. Бонусом до цього йде ще й ваша здатність розбиратися у продуктивності запитів.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ