Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django subquery with aggregate

Tags:

django

I have two models called User and Transaction . Here i want to get the all the users with total sum of the transaction amount where status is success.

I have tried with subquery but i am not getting how to annotate the aggregate of the subquery with conditions

class User(models.Model):
  name = models.CharField(max_length=128)

class Transaction(models.Model):
  user = models.ForeignKey(User)
  status = models.CharField(choices=(("success", "Success"),("failed", "Failed")))
   amount = models.DecimalField(max_digits=10, decimal_places=2)

subquery = Transaction.objects.filter(status="success", user=OuterRef('pk')).aggregate(total_spent = Coalesce(Sum('amount'), 0))

query = User.objects.annotate(total_spent=Subquery(subquery:how to do here ?)).order_by(how to order here by total_spent)
like image 762
coder Avatar asked Apr 30 '19 16:04

coder


People also ask

How do I perform an aggregation within a subquery?

This is the only way to perform an aggregation within a Subquery, as using aggregate () attempts to evaluate the queryset (and if there is an OuterRef, this will not be possible to resolve). Sometimes database expressions can’t easily express a complex WHERE clause. In these edge cases, use the RawSQL expression. For example:

Why do we use as_sqlServer() in Django?

We use as_sqlserver () because django.db.connection.vendor returns sqlserver for the backend. Third-party backends can register their functions in the top level __init__.py file of the backend package or in a top level expressions.py file (or package) that is imported from the top level __init__.py.

How to include multiple windows in a query in Django ORM?

You can specify multiple windows in the same query which in Django ORM would be equivalent to including multiple expressions in a QuerySet.annotate () call. The ORM doesn’t make use of named windows, instead they are part of the selected columns. Defaults to False.

What is an aggregate expression in SQL?

An aggregate expression is a special case of a Func () expression that informs the query that a GROUP BY clause is required. All of the aggregate functions , like Sum () and Count (), inherit from Aggregate (). Since Aggregate s are expressions and wrap expressions, you can represent some complex computations:


1 Answers

This is made a lot easier with the django-sql-utils package.

from django.db.models import Sum,
from sql_util.utils import SubqueryAggregate

User.objects.annotate(
    total_spend=SubqueryAggregate('transaction__amount',
                                  filter=Q(status='success'),
                                  aggregate=Sum)
)

If you want to do it the long way (without django-sql-utils), you need to know these two things about the subquery:

  1. It can't be evaluated before it is used

  2. It can only return a single record with a single column

So, you can't call aggregate on the subquery, because this evaluates the subquery immediately. Instead you have to annotate the value. You also have to group by the outer ref value, otherwise you'll just annotate each Transaction independently.

subquery = Transaction.objects.filter(
        status='success', user=OuterRef('pk')
    ).values(
        'user__pk'
    ).annotate(
        total_spend=Sum('amount')
    ).values(
        'total_spend'
    )

The first .values causes the correct group by. The second .values causes selecting the one value that you want.

like image 63
Brad Martsberger Avatar answered Oct 16 '22 02:10

Brad Martsberger