带有聚合的 Django 子查询

cod*_*der 9 django

我有两个模型称为UserTransaction。在这里,我想获取状态为成功的交易金额总和的所有用户。

我已尝试使用子查询,但我不知道如何使用条件注释子查询的聚合

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)
Run Code Online (Sandbox Code Playgroud)

小智 27

使用django-sql-utils包,这变得容易多了。

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)
)
Run Code Online (Sandbox Code Playgroud)

如果你想做得很长(没有 django-sql-utils),你需要知道关于子查询的这两件事:

  1. 在使用之前无法对其进行评估

  2. 它只能返回单列的单条记录

因此,您不能调用aggregate子查询,因为这会立即评估子查询。相反,您必须注释该值。您还必须按外部 ref 值分组,否则您只需独立注释每个事务。

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

第一个.values导致正确的分组依据。第二个.values原因是选择您想要的一个值。


Sla*_*ava 12

你可以这样做:

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')
Run Code Online (Sandbox Code Playgroud)

有关此方法的更多详细信息,您可以在此答案中看到:/sf/answers/4831451271/


小智 -2

要使用子查询,请使用:

query=User.objects.annotate(total_spent=Subquery(subquery.values("user")[:1])).order_by("total_spent")
Run Code Online (Sandbox Code Playgroud)