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