from django.db.models import Count, Avg, Sum, Q, F
from django.views.generic import TemplateView
from products.models import Product, Order, OrderItem
class DashboardView(TemplateView):
template_name = 'analytics/dashboard.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Simple aggregates
stats = Order.objects.aggregate(
total_revenue=Sum('total_amount'),
average_order=Avg('total_amount'),
order_count=Count('id')
)
# Annotate each product with sales stats
products = Product.objects.annotate(
total_sold=Sum('orderitem__quantity'),
revenue=Sum(F('orderitem__quantity') * F('orderitem__price')),
order_count=Count('orderitem__order', distinct=True)
).filter(
total_sold__isnull=False
).order_by('-revenue')[:10]
# Conditional aggregation
order_stats = Order.objects.aggregate(
completed=Count('id', filter=Q(status='completed')),
pending=Count('id', filter=Q(status='pending')),
cancelled=Count('id', filter=Q(status='cancelled'))
)
context.update({
'stats': stats,
'top_products': products,
'order_stats': order_stats,
})
return context
Aggregation performs database-level calculations efficiently. I use aggregate() for single results across entire queryset (like average or total) and annotate() to add calculated fields to each object. Common aggregates include Count, Sum, Avg, Min, and Max. I combine annotate() with filter() to create conditional aggregates. F expressions reference fields in aggregations. This pushes computation to the database, which is much faster than Python loops. For complex stats, I sometimes use raw SQL via extra() or direct cursor, but aggregates handle most cases elegantly.