Search code examples
djangodjango-aggregation

Django: Group by date (day, month, year)


I've got a simple Model like this:

class Order(models.Model):
    created = model.DateTimeField(auto_now_add=True)
    total = models.IntegerField() # monetary value

And I want to output a month-by-month breakdown of:

  • How many sales there were in a month (COUNT)
  • The combined value (SUM)

I'm not sure what the best way to attack this is. I've seen some fairly scary-looking extra-select queries but my simple mind is telling me I might be better off just iterating numbers, starting from an arbitrary start year/month and counting up until I reach the current month, throwing out simple queries filtering for that month. More database work - less developer stress!

What makes most sense to you? Is there a nice way I can pull back a quick table of data? Or is my dirty method probably the best idea?

I'm using Django 1.3. Not sure if they've added a nicer way to GROUP_BY recently.


Solution

  • Django 1.10 and above

    Django documentation lists extra as deprecated soon. (Thanks for pointing that out @seddonym, @Lucas03). I opened a ticket and this is the solution that jarshwah provided.

    from django.db.models.functions import TruncMonth
    from django.db.models import Count
    
    Sales.objects
        .annotate(month=TruncMonth('created'))  # Truncate to month and add to select list
        .values('month')                          # Group By month
        .annotate(c=Count('id'))                  # Select the count of the grouping
        .values('month', 'c')                     # (might be redundant, haven't tested) select month and count 
    

    Older versions

    from django.db import connection
    from django.db.models import Sum, Count
    
    truncate_date = connection.ops.date_trunc_sql('month', 'created')
    qs = Order.objects.extra({'month':truncate_date})
    report = qs.values('month').annotate(Sum('total'), Count('pk')).order_by('month')
    

    Edits

    • Added count
    • Added information for django >= 1.10