[Django]-Django and Aggregate: Sum of distinct values?

10👍

Django 3.0 introduced “distinct=True” on Sum and Avg:
https://docs.djangoproject.com/en/3.0/ref/models/querysets/#sum

21👍

From this answer for a related question:

from django.db.models import Sum
income_posts.values('category__name').order_by('category__name').annotate(total=Sum('amount'))

13👍

Just to add to arjun27’s answer. Since that package seems to have been abandoned you might want to just copy past the 3 lines you need from it:

from django.db.models import Sum
class DistinctSum(Sum):
    function = "SUM"
    template = "%(function)s(DISTINCT %(expressions)s)"

Which can be used the same as above:

income_posts.annotate(total=DistinctSum('amount')

9👍

I think this issue also related to Combining multiple aggregations.

Here is the ticket for this bug.

We can use Subquery(Django Docs) to achieve this issue:

from django.db.models import Subquery, OuterRef, IntegerField, Sum, Value, Count

MyModel.objects.annotate(
    count_model_a=Count('ModelA', distinct=True), 
    sum_model_b=Coalesce(
        Subquery(
            ModelB.objects.filter(
                MyModel=OuterRef('pk')
            ).values('MyModel_id').annotate(
                my_sum=Sum('MyModel_Field')
            ).values('my_sum')[:1],
            output_field=IntegerField()
        ),
        Value(0)
    )
).values("count_model_a", "sum_model_b")

I also used Coalesce(Django Docs) function to prevent returning None.

The above code will run one query to DB.

👤NKSM

4👍

If you are on Postgres, you can use the django-pg-utils package for sum of distinct values.

from pg_utils import DistinctSum
income_posts.annotate(total=DistinctSum('amount')

2👍

For those who are using django 2.2 LTE, this behavior can be achieved just reproducing django 3.0 commit that implements distinct for Sum:

https://github.com/django/django/commit/5f24e7158e1d5a7e40fa0ae270639f6a171bb18e

this way:

from django.db.models Sum

class SumDistinctHACK(Sum):
    allow_distinct = True

and now you can use the django 3.0 syntax:

queryset.annotate(
    sum_result=SumDistinctHACK(
        'relatedmodel__values_to_sum',
        distinct=True,
    )
)

remember to replace SumDistinctHACK to Sum if you upgrade to django >= 3.0

👤Nache

1👍

For older version of Django use Func

queryset.annotate(
   sum_result=Sum(
        Func(F('amount'), function='DISTINCT')
   )
)

0👍

You can do this:

income_posts.values("category__name").distinct().annotate(total=Sum("amount"))
👤sdil

Leave a comment