加入并与Django ORM求和

时间:2014-03-23 19:48:59

标签: django django-models django-views

我有两个型号

class Model1(models.Model):
    name = models.CharField()

class Model2(models.Model):
    model1 = models.ForeignKey(Model1)
    price = models.IntegerField()

如何在这两个模型之间进行简单连接?我希望Model1中的所有对象都具有Model2中与特定Model1对象相关的所有对象的价格总和。

我想这就像是

queryset = Model1.objects.annotate(model2_price=Sum(price)).values('name','model2_price')

1 个答案:

答案 0 :(得分:0)

您已使用注释关闭了。只需在Sum()中使用有效的查找字符串即可:

queryset = Model1.objects.annotate(model2_price=Sum('model1__price'))

for m in queryset:
    print m.model2_price

编辑

我忽略了ForeignKeyModel2Model1,但这绝对没有问题。在模型中,Django自动为所有OneToOneFieldForeignKeyManyToManyField添加反向关系。您可以将related_name参数传递给其中每个字段以更改反向关系的属性名称,但默认情况下它将为<lowercase_modelname>_set。所以你现在需要做的是:

queryset = Model1.objects.annotate(model2_price=Sum('model2_set__price'))

我通常建议明确定义相关名称并使用它来引用相关对象,以避免混淆属性名称,并可能为关系提供更具描述性的名称:

class Model2(models.Model):
    model1 = models.ForeignKey(Model1, related_name='model2')
    price = models.IntegerField()

queryset = Model1.objects.annotate(model2_price=Sum('model2__price'))

编辑2 : 查询的结果无法修改,注释不支持更复杂的查询。有几种方法可以解决这个问题,我最喜欢这个:

class Model1(models.Model):
    name = models.CharField()

    @property
    def model2_total_price(self):
        if not hasattr(self, '_model2_total_price'):
            raise AttributeError("'%s' object has no attribute 'model2_total_price'" % self.__class__.__name__)
        if self._model2_total_price:
            return self._model2_total_price
        return 0

queryset = Model1.objects.annotate(_model2_total_price=Sum('model2_set__price'))

for m in queryset:
    print m.model2_total_price

这与您注释名为model2_total_price的字段的行为完全相同,包括如果根本不使用注释则引发AttributeError,而如果0则仍然返回model2_set 1}}是空的。 @property装饰器允许您使用实例方法,就像它是常规属性一样(尽管您无法在不提供额外的setter方法的情况下进行设置)。 _model2_total_price中的起始下划线告诉其他开发者远离该属性,除非他们确切地知道他们正在做什么以及他们为什么要改变它(替换正确的私有属性)在其他语言中,没有强制执行 - 这是Pythonesque的做法。

您唯一需要确保的是始终为注释使用完全相同的属性名称:在此示例中为_model2_total_price