40

I have a list of values paid and want to display the total paid. I have used aggregation and Sum to calculate the values together. The problem is,I just want the total value printed out, but aggregation prints out: {'amount__sum': 480.0} (480.0 being the total value added.

In my View, I have:

    from django.db.models import Sum

    total_paid = Payment.objects.all.aggregate(Sum('amount'))

And to show the value on the page, I have a mako template with the following:

    <p><strong>Total Paid:</strong> ${total_paid}</p>

How would I get it to show 480.0 instead of {'amount__sum': 480.0}?

djvg
  • 11,722
  • 5
  • 72
  • 103
Stephen
  • 527
  • 2
  • 7
  • 19

6 Answers6

63

I don't believe there is a way to get only the value.

You could just do ${{ total_paid.amount__sum }} in your template. Or do total_paid = Payment.objects.all().aggregate(Sum('amount')).get('amount__sum', 0.00) in your view.

EDIT

As others have pointed out, .aggregate() will always return a dictionary with all of the keys from the aggregates present, so doing .get() on the result is not necessary. However, if the queryset is empty, each aggregate value would be None. So depending on your code, if you are expecting a float, you could do:

total_paid = Payment.objects.all().aggregate(Sum('amount'))['amount__sum'] or 0.00

jproffitt
  • 6,225
  • 30
  • 42
  • Thank you for this. I had already tried ${{ total_paid.amount__sum }} but it didnt work. But calling it in the View worked nicely! – Stephen Oct 02 '13 at 14:08
  • @jproffitt, could you give me an example when you will not have a dictionary with the key `'amount__sum'` if the aggregation executes successfully? – lmiguelvargasf Jan 07 '18 at 03:09
  • @lmiguelvargasf You are correct. There is no need to do `.get`. I have made an edit to my answer. – jproffitt Jan 08 '18 at 17:00
34

Give it a name and then ask for it:

total_paid = Payment.objects.all.aggregate(sum=Sum('amount'))['sum']

Should be little more readable, and there is no need for conversion.

mehmet
  • 7,720
  • 5
  • 42
  • 48
25

The aggregate() method returns a dictionary. If you know you're only returning a single-entry dictionary you could use .values()[0].

In Python 2:

total_paid = Payment.objects.aggregate(Sum('amount')).values()[0]

In Python 3, (thanks @lmiguelvargasf) this will need to be:

total_paid = list(Payment.objects.aggregate(Sum('amount')).values())[0]

The end result is the same as @jproffitt's answer, but it avoids repeating the amount__sum part, so it's a little more generic.

nimasmi
  • 3,978
  • 1
  • 25
  • 41
  • 2
    This is my preferred solution too. `values()` has [changed](http://stackoverflow.com/questions/17431638/get-typeerror-dict-values-object-does-not-support-indexing-when-using-python) in Python 3 to return a view rather than a list. Passing the return of `values()` through `list()` makes this solution work then. – Taylor D. Edmiston Nov 05 '16 at 19:50
  • 1
    I agree with @TaylorEdmiston, this code is not working on Python 3. I have added an answer that solves it. – lmiguelvargasf Jan 07 '18 at 03:09
  • Thank you @lmiguelvargasf. To help others in the future, I have also just submitted a community edit to this answer adding the line for python 3. I believe it will be visible to others after it's peer approved. – Taylor D. Edmiston Jan 07 '18 at 03:40
3

In Python 3:

You can solve it by converting the dict_values to a list:

total_paid = list(Payment.objects.aggregate(Sum('amount')).values())[0] or 0 # the or 0 is required in case the query is an empty query set.

The previous code avoids using 'column_name__sum' as key, but in case you prefer the dictionary way:

total_paid = Payment.objects.aggregate(Sum('amount'))['amount__sum'] or 0

In terms of efficiency, I made a test with some data I have, and it seems that using the dictionary key is faster:

In [9]: %timeit total = Pledge.objects.filter(user=user, group__isnull=True).aggregate(Sum('amount'))['amount__sum'] or 0
3.13 ms ± 25.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [10]: %timeit total = list(Pledge.objects.filter(user=user, group__isnull=True).aggregate(Sum('amount')).values())[0] or 0
3.22 ms ± 61.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In terms of readability, I think that @mehmet's solution is the best one, and I have also test its efficiency:

In [18]: %timeit Pledge.objects.filter(user=user, group__isnull=True).aggregate(sum=Sum('amount'))['sum'] or 0
3.22 ms ± 124 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
lmiguelvargasf
  • 63,191
  • 45
  • 217
  • 228
0

(It's only useful if you used decimal field) Store a key value in a new variable and convert it to the float if you're using a decimal. I think it's the easiest way

total_paid = Payment.objects.all.aggregate(Sum('amount'))
# output: {'amount__sum':Decimal('0000.000000')
tp=total_paid['amount__sum']
new_total_paid=float(tp)
0

100% you can solve your demand by this technique

from django.db.models import Sum

sum_count = Payment.objects.aggregate(Sum('amount'))
totalAmonut = sum_count['Qty__sum']
Josef
  • 2,869
  • 2
  • 22
  • 23
Mithun Rana
  • 1,334
  • 1
  • 9
  • 10