我正在使用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'
处理此问题的正确方法是什么?
答案 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
。