Django:根据字段的状态对 Sum Case 进行注释
Posted
技术标签:
【中文标题】Django:根据字段的状态对 Sum Case 进行注释【英文标题】:Django: annotate Sum Case When depending on the status of a field 【发布时间】:2018-11-28 12:16:58 【问题描述】:在我的应用程序中,我需要获取过去 30 天内每天的所有交易。
在交易模型中,我有一个货币字段,如果选择的货币是英镑或美元,我想将值转换为欧元。
models.py
class Transaction(TimeMixIn):
COMPLETED = 1
REJECTED = 2
TRANSACTION_STATUS = (
(COMPLETED, _('Completed')),
(REJECTED, _('Rejected')),
)
user = models.ForeignKey(CustomUser)
status = models.SmallIntegerField(choices=TRANSACTION_STATUS, default=COMPLETED)
amount = models.DecimalField(default=0, decimal_places=2, max_digits=7)
currency = models.CharField(max_length=3, choices=Core.CURRENCIES, default=Core.CURRENCY_EUR)
到目前为止,这是我一直在使用的:
Transaction.objects.filter(created__gte=last_month, status=Transaction.COMPLETED)
.extra("date": "date_trunc('day', created)")
.values("date").annotate(amount=Sum("amount"))
它返回一个查询集,其中包含带有日期和数量的字典:
<QuerySet ['date': datetime.datetime(2018, 6, 19, 0, 0, tzinfo=<UTC>), 'amount': Decimal('75.00')]>
这就是我现在尝试的:
queryset = Transaction.objects.filter(created__gte=last_month, status=Transaction.COMPLETED).extra("date": "date_trunc('day', created)").values('date').annotate(
amount=Sum(Case(When(currency=Core.CURRENCY_EUR, then='amount'),
When(currency=Core.CURRENCY_USD, then=F('amount') * 0.8662),
When(currency=Core.CURRENCY_GBP, then=F('amount') * 1.1413), default=0, output_field=FloatField()))
)
它将 gbp 或 usd 转换为欧元,但它在同一天创建了 3 个字典,而不是对它们求和。
这是它返回的内容:<QuerySet ['date': datetime.datetime(2018, 6, 19, 0, 0, tzinfo=<UTC>), 'amount': 21.655, 'date': datetime.datetime(2018, 6, 19, 0, 0, tzinfo=<UTC>), 'amount': 28.5325, 'date': datetime.datetime(2018, 6, 19, 0, 0, tzinfo=<UTC>), 'amount': 25.0]>
这就是我想要的:
<QuerySet ['date': datetime.datetime(2018, 6, 19, 0, 0, tzinfo=<UTC>), 'amount': 75.1875]>
【问题讨论】:
【参考方案1】:唯一剩下的是order_by
。这将(是的,我知道这听起来很奇怪),迫使 Django 执行GROUP BY
。所以应该改写为:
queryset = Transaction.objects.filter(
created__gte=last_month,
status=Transaction.COMPLETED
).extra(
"date": "date_trunc('day', created)"
).values(
'date'
).annotate(
amount=Sum(Case(
When(currency=Core.CURRENCY_EUR, then='amount'),
When(currency=Core.CURRENCY_USD, then=F('amount') * 0.8662),
When(currency=Core.CURRENCY_GBP, then=F('amount') * 1.1413),
default=0,
output_field=FloatField()
))
).order_by('date')
(我在这里稍微修正了格式以使其更具可读性,尤其是对于小屏幕,但它(如果我们忽略间距)与问题中的相同,当然 .order_by(..)
除外。)
【讨论】:
【参考方案2】:我们需要聚合查询集以完成您正在尝试的操作。
尝试使用 aggregate()
queryset = Transaction.objects.filter(created__gte=last_month, status=Transaction.COMPLETED).extra("date": "date_trunc('day', created)").values('date').aggregate(
amount=Sum(Case(When(currency=Core.CURRENCY_EUR, then='amount'),
When(currency=Core.CURRENCY_USD, then=F('amount') * 0.8662),
When(currency=Core.CURRENCY_GBP, then=F('amount') * 1.1413), default=0, output_field=FloatField())))
更多信息:aggregate()
【讨论】:
但这将汇总所有行,而不是通过date
将它们分开。
聚合将只返回时间的总和,而不给出日期以上是关于Django:根据字段的状态对 Sum Case 进行注释的主要内容,如果未能解决你的问题,请参考以下文章
postgreSQL计算总数sum if case when
golang实现es根据某个字段分组,对某个字段count总数,对某个字段sum求和
golang实现es根据某个字段分组,对某个字段count总数,对某个字段sum求和
golang实现es根据某个字段分组,对某个字段count总数,对某个字段sum求和