💻 Примеры: Практикум 6
⚡ Ключевые примеры
# Sum F*F
Product.objects.aggregate(total=Sum(F('price') * F('quantity')))['total']
# Avg + group
Product.objects.values('category__name').annotate(avg=Avg('price'))
# ExpressionWrapper
ProductDetail.objects.annotate(
lifetime=ExpressionWrapper(F('expiration_date') - F('manufacturing_date'),
output_field=fields.DurationField())
).aggregate(avg=Avg('lifetime'))['avg'].days
# order_by annotated
Order.objects.annotate(total=Sum(F('order_items__price')*F('order_items__quantity'))).order_by('-total')
# timezone + filter
Order.objects.filter(order_date__gte=timezone.now() - timezone.timedelta(days=30))
# Срез
Product.objects.order_by('-price')[:10]
Пример 1: Общая стоимость через Sum(F*F)
Перемножаем два поля на уровне SQL и суммируем:
from django.db.models import Sum, F
from store.models import Product
total_value = Product.objects.aggregate(
total_value=Sum(F('price') * F('quantity'))
)['total_value']
print(f"Общая стоимость всех продуктов: {total_value}")
# Общая стоимость всех продуктов: 158432.50
Пример 2: Группировка + Avg по категориям
Паттерн values().annotate() — аналог SQL GROUP BY:
from django.db.models import Avg
from store.models import Product
average_price_by_category = Product.objects.values('category__name').annotate(
average_price=Avg('price')
).order_by('category__name')
for entry in average_price_by_category:
print(f"Категория: {entry['category__name']}, Средняя цена: {entry['average_price']}")
# Категория: Electronics, Средняя цена: 1250.00
# Категория: Furniture, Средняя цена: 450.00
Пример 3: ExpressionWrapper для разницы дат
Разница между двумя DateField требует явного output_field:
from django.db.models import Avg, F, ExpressionWrapper, fields
from store.models import ProductDetail
average_lifetime = ProductDetail.objects.annotate(
lifetime=ExpressionWrapper(
F('expiration_date') - F('manufacturing_date'),
output_field=fields.DurationField()
)
).aggregate(average_lifetime=Avg('lifetime'))['average_lifetime']
print(f"Средняя продолжительность жизни продуктов: {average_lifetime.days} дней")
# Средняя продолжительность жизни продуктов: 365 дней
Пример 4: Сортировка по аннотированному полю
annotate() создаёт виртуальное поле, по которому можно сортировать:
from django.db.models import Sum, F, Count
from store.models import Order
# Сортировка по вычисленной сумме заказа
orders_by_total = Order.objects.annotate(
total=Sum(F('order_items__price') * F('order_items__quantity'))
).order_by('-total')
for order in orders_by_total:
print(f"Заказ {order.id}, Общая стоимость: {order.total}")
# Сортировка по количеству позиций
orders_by_items = Order.objects.annotate(
item_count=Count('order_items')
).order_by('-item_count')
for order in orders_by_items:
print(f"Заказ {order.id}, Позиций: {order.item_count}")
Пример 5: Фильтр по временным меткам
from django.utils import timezone
from store.models import Order
one_month_ago = timezone.now() - timezone.timedelta(days=30)
recent_orders = Order.objects.filter(order_date__gte=one_month_ago)
print("Заказы за последний месяц:")
for order in recent_orders:
print(f"Заказ {order.id}, Дата: {order.order_date}")
Пример 6: Срезы — LIMIT в QuerySet
from store.models import Product
# Первые 5 продуктов — SQL: SELECT ... LIMIT 5
first_five = Product.objects.all()[:5]
for product in first_five:
print(f"{product.name}: {product.price}")
# Топ-10 дорогих — SQL: SELECT ... ORDER BY price DESC LIMIT 10
top_expensive = Product.objects.order_by('-price')[:10]
for product in top_expensive:
print(f"{product.name}: {product.price}")
Пример 7: Сложная агрегация — заказы по клиентам
Группировка по нескольким полям клиента + Count + Sum одновременно:
from django.db.models import Count, Sum, F
from store.models import Order
orders_summary = Order.objects.values(
'customer__first_name',
'customer__last_name'
).annotate(
order_count=Count('id'),
total_spent=Sum(F('order_items__price') * F('order_items__quantity'))
).order_by('customer__last_name')
for entry in orders_summary:
print(
f"Клиент: {entry['customer__first_name']} {entry['customer__last_name']}, "
f"Заказов: {entry['order_count']}, "
f"Общая сумма: {entry['total_spent']}"
)