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聚合:两个字段相乘的总和的主要内容,如果未能解决你的问题,请参考以下文章