属性/计算字段的聚合

时间:2015-10-28 15:56:42

标签: django

假设我有跟随(简化)模型,其中Car是加权的。有时车内有人(但并非总是如此),因此我有@property来计算汽车的net_weight

class CarWeight(models.Model):
    total_weight = models.DecimalField(max_digits=5, decimal_places=1)
    person_weight = models.DecimalField(max_digits=5, decimal_places=1, null=True, blank=True)

    @property
    def net_weight(self):
        if self.person_weight:
            return self.total_weight - self.person_weight
        else:
            return self.total_weight

    def __unicode__(self):
        if self.person_weight > 0:
            return u"%s (excl. persons)" % (self.total_weight - self.person_weight)
        else:
            return u"%s" % self.total_weight

现在,我想获得CarWeight个对象的最小,最大和平均净重。

我以为我可以CarWeight.objects.aggregate(Avg('net_weight'), Max('net_weight'), Min('net_weight')),但这会产生Cannot resolve keyword 'net_weight' into field。 显然,不支持属性作为参数(仅实际字段)。

我搜索了SO,发现我可能需要使用.extra(),但无法让它发挥作用。

注意:我不想反规范化(通过存储net_weight并覆盖save())。

使用Django 1.8和Postgres。

修改

正如所建议的那样,当我首先尝试使用聚合然后进行注释时,我会得到错误的结果:

>>> CarWeight.objects.all()
[<CarWeight: 100.0>, <CarWeight: 120.0>]
>>> CarWeight.objects.annotate(net_val=Sum('total_weight')-Sum('person_weight'))
[<CarWeight: 100.0>, <CarWeight: 120.0>]
>>> CarWeight.objects.annotate(net_val=Sum('total_weight')-Sum('person_weight')).aggregate(Avg('net_val'), Max('net_val'), Min('net_val'))
{'net_val__avg': None, 'net_val__max': None, 'net_val__min': None}
>>> CarWeight.objects.create(total_weight=500, person_weight=100)
<CarWeight: 400 (excl. persons)>
>>> CarWeight.objects.annotate(net_val=Sum('total_weight')-Sum('person_weight')).aggregate(Avg('net_val'), Max('net_val'), Min('net_val'))
{'net_val__avg': 400.0, 'net_val__max': Decimal('400.0'), 'net_val__min': Decimal('400.0')}
>>> CarWeight.objects.annotate(net_val=Sum('total_weight')-Sum('person_weight'))
[<CarWeight: 400.0 (excl. persons)>, <CarWeight: 100.0>, <CarWeight: 120.0>]

EDIT2

此方法的解决方法是默认情况下将person_weight设置为“0”而不是空。然后计算可以毫无问题地进行。

person_weight = models.DecimalField(max_digits=5, decimal_places=1, default=0)

1 个答案:

答案 0 :(得分:1)

尝试:

result = CarWeight.objects.annotate(net_val=Sum('total_weight')-Sum('person_weight')).aggregate(Avg('net_val'),Max('net_val'),Min('net_val'))