Search code examples
djangodjango-modelsdjango-querysetdjango-ormdjango-annotate

Django ORM, Sum column values through query


I have a table with the following values:

user amount date
John 10 2017-07-01
John 20 2019-07-01
John 30 2020-09-01
John 40 2021-11-01
... ... ...
Dan -20 2019-02-01
Dan -30 2020-04-01
Dan -40 2021-06-01

The input of the function is a range of date, for example, as follows:

date_start = '2019-01-01'
date_end = '2021-11-07'

Expected output:

For all users, for each date in this range of date, I want to return the sum of the amounts, from the current date to all previous dates like this:

user amount date
John 30 (10+20) 2019-07-01
John 60 (10+20+30) 2020-09-01
John 100 (10+20+30+40) 2021-11-01
... ... ...
Dan -20 2019-02-01
Dan -50 (-20-30) 2020-04-01
Dan -90 (-20-30-40) 2021-06-01

My efforts

def get_sum_amount(self, date_start=None, date_end=None):
    date_detail = {}
    # Minimum date
    if date_start:
        date_detail['date__gt'] = date_start

    # Maximum date
    if date_end:
        date_detail['date__lt'] = date_end

    detail = Financial.objects.filter(Q(**date_detail)) \
        .values('user') \
        .annotate(sum_amount=Coalesce(Sum(F('amount')), Value(0)))

but no result.

UPDATED

The output of the function for the above example is:

user amount
John 100
Dan -90

Solution

  • You can use Window functions with a partition onuser to run a cumulative sum per user, ordered by the date:

    detail = Financial.objects.filter(**date_detail).annotate(
        running_amount=Window(
            expression=Sum('amount'),
            partition_by=[F('user')],
            order_by=F('date').asc(),
        )
    )