[Fixed]-Django annotate price per month

1πŸ‘

βœ…

My suggestion is that you add a field to your model, as it isn’t possible to use __month in .values_list. Values_list can be used to group your queries before annotation.

Field could be something like.

year_month = models.CharField(index=True)

You could overwrite the save method on your model and do something like

def save(self, *args, **kwargs)
    # Not sure if auto_add_now already set the value, could use timezone.now()
    if not self.id:  # thus not saved yet
        self.year_month = self.created_at.strftime("%Y%m")
    super(Model, self).save(*args, **kwargs)

Now its possible to annotate your prices.

Project.objects.values_list('year_month').annotate(price_sum=Sum("price_aux"))

If you need more info, I believe your could expand the values_list.

Project.objects.values_list('year_month').annotate(price_sum=Sum("price_aux")) \
    .values_list('name', 'year_month', 'price_sum')

Doing it without adding field is ofcourse possible. Let say you want the past year.

import datetime
from django.utils import timezone

def my_list_view(request):
    end = timezone.now()
    # note, could do some weird stuff in years with 366 days
    begin = (end - datetime.timedelta(days=365)).replace(day=1)  

    container = {}
    while begin < end:
        container[begin.strftime("%Y%m")] = 0
        begin = (begin + datetime.timedelta(weeks=5).replace(day=1)

    for project in Project.objects.iterator():
        container[project.created_at.strftime("%Y%m")] += price

    # Now you can render your template
πŸ‘€Blackeagle52

0πŸ‘

Say your model is called Prices and has two fields date and price where date is a datetime field. Then you can use date__month (note the double underscore) as a keyword argument to query methods (see the docs for field lookups).

You can retrieve the price by month via

Prices.objects.get(date__month=1).price
πŸ‘€a_guest

Leave a comment