56👍
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.
12👍
You can do it like this:
subquery = Transaction.objects.filter(
status="success", user=OuterRef('pk')
).annotate(
total_spent = Coalesce(Func('amount', function='Sum'), Decimal(0))
).values('total_spent')
query = User.objects.annotate(
total_spent=Subquery(subquery)
).order_by('total_spent')
The more details on this method you can see in this answer: https://stackoverflow.com/a/69020732/10567223
- [Django]-Why am I getting this error in Django?
- [Django]-Difference between 'related_name' and 'related_query_name' attributes in Django?
- [Django]-Oauth for Google API example using Python / Django
0👍
The suggested solution didn’t work for me when there was ordering set on the model.
class InstallmentReservation(models.Model):
class Meta:
ordering = ['id']
I needed to clear the ordering to make it work again.
subquery.query.clear_ordering(True)
Whole code example – a method on a queryset – hope it helps
def with_installment_reservations_amounts(self):
"""
Sum of initial amount of active installment reservations annotated in _installment_reservations_initial_amount
Sum of principal amount of active installment reservations annotated in _installment_reservations_amount
`.values('customer')` in subquery is used to properly sum values. See https://stackoverflow.com/questions/55925437/django-subquery-with-aggregate for more details.
also this does not work when there is an ordering set on a model for some reason, so we need to clear it.
"""
reservation_query = InstallmentReservation.objects.filter(customer_id=OuterRef('pk')).active().values('customer')
reservation_query.query.clear_ordering(True)
return self.annotate(
_installment_reservations_amount=Coalesce(Subquery(reservation_query.annotate(sum=Sum('amount_principal')).values('sum')[:1]), Decimal(0),),
_installment_reservations_initial_amount=Coalesce(Subquery(reservation_query.annotate(sum=Sum('initial_installment_amount')).values('sum')[:1]), Decimal(0),),
)
- [Django]-How to redirect with messages to display them in Django Templates?
- [Django]-Django query annotation with boolean field
- [Django]-How to run gunicorn from a folder that is not the django project folder
-1👍
You can hit this query:
from django.db.models import Avg, Count, Min, Sum
User.objects.filter(status="success").annotate(total_amount=Sum('transaction__amount'))
- [Django]-How to remove the prefix of a table for a Django model?
- [Django]-Can Django automatically create a related one-to-one model?
- [Django]-Django admin ManyToMany inline "has no ForeignKey to" error
-2👍
For using the subquery, use this:
query=User.objects.annotate(total_spent=Subquery(subquery.values("user")[:1])).order_by("total_spent")
- [Django]-'EntryPoints' object has no attribute 'get' – Digital ocean
- [Django]-Import "rest_framework" could not be resolved. But I have installed djangorestframework, I don't know what is going wrong
- [Django]-Django: what is the difference (rel & field)