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

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

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

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

【问题讨论】:

    标签: python django django-models django-queryset django-orm


    【解决方案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')))
      

    【讨论】:

    【解决方案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 年

    你有几种选择:

    1. Raw query
    2. Emulbreh's undocumented approach
    3. 创建第三个字段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 或附加属性作为最后一个选项。无论如何,谢谢。
    猜你喜欢
    • 2012-05-04
    • 2023-03-16
    • 1970-01-01
    • 2010-12-24
    • 1970-01-01
    • 2014-11-09
    • 1970-01-01
    • 1970-01-01
    • 2018-10-03
    相关资源
    最近更新 更多