Django:根据字段状态注释总和案例

时间:2018-06-19 13:43:23

标签: python django datetime django-queryset

在我的应用程序中,我需要获取过去30天每天的所有交易。

在交易模型中,我有一个货币字段,如果选择的货币是GBP或USD,我想将其转换为欧元。

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}]>

2 个答案:

答案 0 :(得分:2)

唯一保留的是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(..)除外。)< / p>

答案 1 :(得分:0)

我们需要聚合查询集以完成您要尝试的工作。

尝试使用 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()