带有聚合的 Django 子查询
Posted
技术标签:
【中文标题】带有聚合的 Django 子查询【英文标题】:Django subquery with aggregate 【发布时间】:2019-04-30 16:45:12 【问题描述】:我有两个模型,称为 User
和 Transaction
。在这里,我想获取状态为成功的交易金额总和的所有用户。
我尝试过使用子查询,但我不知道如何使用条件注释子查询的聚合
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)
【问题讨论】:
【参考方案1】: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)
)
如果你想长期做下去(没有 django-sql-utils),你需要知道关于子查询的这两件事:
使用前无法评估
只能返回单列单条记录
因此,您不能在子查询上调用aggregate
,因为这会立即评估子查询。相反,您必须注释该值。您还必须按外部 ref 值进行分组,否则您只需单独注释每个 Transaction。
subquery = Transaction.objects.filter(
status='success', user=OuterRef('pk')
).values(
'user__pk'
).annotate(
total_spend=Sum('amount')
).values(
'total_spend'
)
第一个 .values
导致正确的分组依据。第二个.values
导致选择您想要的一个值。
【讨论】:
请问,如果amount
也是一个注释字段呢? __amount
似乎不适用于这样的子查询字段。
@DingkunLiu,子查询是正确的。里面没有__amount
。如果amount
是Transaction
对象的注释字段,则子查询不会改变【参考方案2】:
你可以这样做:
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')
您可以在此答案中看到有关此方法的更多详细信息:https://***.com/a/69020732/10567223
【讨论】:
如果您遇到奇怪的“分组依据”错误,请将 .order_by 添加到子查询的末尾。 newbedev.com/django-conditional-subquery-aggregate【参考方案3】:你可以点击这个查询:
from django.db.models import Avg, Count, Min, Sum
User.objects.filter(status="success").annotate(total_amount=Sum('transaction__amount'))
【讨论】:
【参考方案4】:当模型上设置了订购时,建议的解决方案对我不起作用。
class InstallmentReservation(models.Model):
class Meta:
ordering = ['id']
我需要清除订单以使其再次工作。
subquery.query.clear_ordering(True)
整个代码示例 - 查询集上的方法 - 希望对您有所帮助
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://***.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),),
)
【讨论】:
【参考方案5】:要使用子查询,请使用:
query=User.objects.annotate(total_spent=Subquery(subquery.values("user")[:1])).order_by("total_spent")
【讨论】:
问题要求 SUM 但不是查询集中的第一个实例。以上是关于带有聚合的 Django 子查询的主要内容,如果未能解决你的问题,请参考以下文章
如何编写带有子查询的 Django 查询作为 WHERE 子句的一部分?
在 Django 的 ORM 中使用带有 UPDATE 的子查询