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)
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:
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.
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.
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:
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:
It can't be evaluated before it is used
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With