【问题标题】:Django: annotate Sum Case When depending on the status of a fieldDjango:根据字段的状态对 Sum Case 进行注释
【发布时间】: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 个字典,而不是对它们求和。

这是它返回的内容:&lt;QuerySet [{'date': datetime.datetime(2018, 6, 19, 0, 0, tzinfo=&lt;UTC&gt;), 'amount': 21.655}, {'date': datetime.datetime(2018, 6, 19, 0, 0, tzinfo=&lt;UTC&gt;), 'amount': 28.5325}, {'date': datetime.datetime(2018, 6, 19, 0, 0, tzinfo=&lt;UTC&gt;), 'amount': 25.0}]&gt;

这就是我想要的:

&lt;QuerySet [{'date': datetime.datetime(2018, 6, 19, 0, 0, tzinfo=&lt;UTC&gt;), 'amount': 75.1875}]&gt;

【问题讨论】:

    标签: python django datetime django-queryset


    【解决方案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将它们分开。
      • 聚合将只返回时间的总和,而不给出日期
      猜你喜欢
      • 1970-01-01
      • 2018-12-04
      • 2022-01-25
      • 2017-10-02
      • 1970-01-01
      • 2018-10-21
      • 1970-01-01
      • 2020-01-18
      • 1970-01-01
      相关资源
      最近更新 更多