Django Aggregate-零值分区

时间:2016-09-07 15:33:34

标签: django django-queryset

我正在使用Django的aggregate query expression来计算一些值。最终值是一个除法表达式,有时可以将零作为分母。如果是这种情况,我需要一种方法来逃避,因此它只返回0。

我尝试了以下内容,因为我一直在使用类似我的注释表达式:

from django.db.models import Sum, F, FloatField, Case, When

def for_period(self, start_date, end_date):
    return self.model.objects.filter(
        date__range=(start_date, end_date)
    ).aggregate(
        sales=Sum(F("value")),
        purchase_cogs=Sum(F('purchase_cogs')),
        direct_cogs=Sum(F("direct_cogs")),
        profit=Sum(F('profit'))
    ).aggregate(
        margin=Case(
            When(sales=0, then=0),
            default=(Sum(F('profit')) / Sum(F('value')))*100
        )
    )

然而,它显然不起作用,因为错误说:

  

'dict'对象没有属性'aggregate'

处理此问题的正确方法是什么?

3 个答案:

答案 0 :(得分:10)

这显然不起作用;因为aggregate返回字典而不是QuerySet(请参阅docs),因此您无法将两个aggregate调用链接在一起。

我认为使用annotate可以解决您的问题。 annotate几乎与aggregate相同,除了它返回一个QuerySet,结果保存为属性而不是返回字典。结果是您可以链接annotate来电,甚至可以致电annotate然后再aggregate

所以我相信:

return self.model.objects.filter(
    date__range=(start_date, end_date)
).annotate(  # call `annotate`
    sales=Sum(F("value")),
    purchase_cogs=Sum(F('purchase_cogs')),
    direct_cogs=Sum(F("direct_cogs")),
    profit=Sum(F('profit'))
).aggregate(  # then `aggregate`
    margin=Case(
        When(sales=0, then=0),
        default=(Sum(F('profit')) / Sum(F('value')))*100
    )
)

应该有用。

希望这有帮助。

答案 1 :(得分:2)

我通过以下方式使它可以工作(在Django 2.0中):

from django.db.models import Case, F, FloatField, Sum, When

aggr_results = models.Result.objects.aggregate(
    at_total_units=Sum(F("total_units")),
    ag_pct_units_sold=Case(
        When(at_total_units=0, then=0),
        default=Sum("sold_units") / (1.0 * Sum("total_units")) * 100,
        output_field=FloatField(),
    ),
)

答案 2 :(得分:1)

您不能将这样的聚合语句链接在一起。文档说:

  

aggregate()是QuerySet的终止子句,在调用时,   返回名称 - 值对的字典。

它返回一个python dict,所以你需要找到一种修改查询的方法来一次完成所有操作。您可能可以将第一个调用替换为annotate,而是返回一个查询集:

  

与aggregate()不同,annotate()不是终结子句。的输出   annotate()子句是QuerySet

至于除0的可能性,你可以将你的代码包装在try catch块中,注意ZeroDivisionError