Django聚合:两个字段相乘的总和

Posted

技术标签:

【中文标题】Django聚合:两个字段相乘的总和【英文标题】:Django Aggregation: Summation of Multiplication of two fields 【发布时间】:2012-08-23 08:05:51 【问题描述】:

我有一个类似这样的模型:

class Task(models.Model):
    progress = models.PositiveIntegerField()
    estimated_days = models.PositiveIntegerField()

现在我想在数据库级别进行Sum(progress * estimated_days) 的计算。使用 Django Aggregation 我可以得到每个字段的总和,但不能得到字段乘法的总和。

【问题讨论】:

【参考方案1】:

使用 Django 1.8 及更高版本,您现在可以将表达式传递给聚合:

 from django.db.models import F

 Task.objects.aggregate(total=Sum(F('progress') * F('estimated_days')))['total']

常量也可以,一切都可以组合:

 from django.db.models import Value

 Task.objects.aggregate(total=Sum('progress') / Value(10))['total']

【讨论】:

我认为这个答案更容易理解和维护。另外,如果这些字段的类型不同,在聚合函数中添加output_field参数 我正在 django 1.7 上尝试,但我收到 AttributeError AttributeError: 'ExpressionNode' object has no attribute 'split',我的 django 版本是 1.7.10 是什么版本 如果progress是其他模型的ForienKey,比如设成Product,并且有一个字段price,应该怎么改呢? Task.objects.aggregate(total=Sum(F('progress.price') * F('estimated_days')))['total'] 不起作用 此解决方案适用于我的场景...谢谢【参考方案2】:

更新:对于 Django >= 1.8,请遵循@kmmbvnr 提供的答案

可以使用 Django ORM:

这是你应该做的:

from django.db.models import Sum

total = ( Task.objects
            .filter(your-filter-here)
            .aggregate(
                total=Sum('progress', field="progress*estimated_days")
             )['total']
         )

注意:如果两个字段的类型不同,比如integer & float,你要返回的类型应该作为Sum的第一个参数传递

这是一个迟到的答案,但我想它会帮助寻找相同的人。

【讨论】:

工作得很好,谢谢,但要注意 field kwarg 没有记录,我在 Django 测试套件中没有找到任何关于它的测试。 “进度”字段有什么作用?我正在尝试找出这段代码 sn-p,因为这正是我所需要的 @Maor 我想你的意思是Sum的第一个参数?那么,正如我提到的if the two fields are of different types, say integer & float, the type you want to return should be passed as the first parameter of Sum @sha256,所以我猜你的意思是 Sum 的第一个参数应该是你希望返回结果的类型的字段的名称?在这种情况下,结果将以字段进度的数据类型返回? @BinojDavid,没错!【参考方案3】:

解决方案取决于 Django 版本。

django

from django.db.models import Sum
MyModel.objects.filter(<filters>).aggregate(Sum('field1', field="field1*field2"))

django >= 1.8

from django.db.models import Sum, F
MyModel.objects.filter(<filters>).aggregate(Sum(F('field1')*F('field2')))

【讨论】:

不同类型字段聚合时,需要使用ExpressionWrapper。请看一下这个解决方案:***.com/questions/55458958/…【参考方案4】:

已编辑(在 Django 1.8 之后)

从 Django 1.8 开始,你可以使用F:

from django.db.models import F. Sum

total = ( 
    Task
    .objects
    .aggregate(total=Sum(F('progress') * F('estimated_days'))) 
    ['total']
)

旧答案(Django 1.8 之前)

这个答案写于 2012 年,django 1.8 发布于 2015 年

你有几种选择:

    Raw query Emulbreh's undocumented approach 创建第三个字段progress_X_estimated_days 并在保存覆盖的方法中更新它。然后通过这个新字段进行聚合。

覆盖:

class Task(models.Model):
   progress = models.PositiveIntegerField()
   estimated_days = models.PositiveIntegerField()
   progress_X_estimated_days = models.PositiveIntegerField(editable=False)

   def save(self, *args, **kwargs):
      progress_X_estimated_days = self.progress * self.estimated_days
      super(Task, self).save(*args, **kwargs)

【讨论】:

是的,我实际上是保留原始 SQL 或附加属性作为最后一个选项。无论如何,谢谢。

以上是关于Django聚合:两个字段相乘的总和的主要内容,如果未能解决你的问题,请参考以下文章

elasticsearch 实现聚合后两个字段相除相加相减相乘,运算

聚合查询中由另一个字段分组的两个 int 数组的总和

django聚合多天

Django聚合,计数总和

注释总和的 Django 聚合平均值(1.6.5)

带有聚合的 Django 子查询