因此,我阅读了带注释的列,并使用了F()函数,我看到了this post上如何求和多个列的信息。但是,我正在使用EAV样式数据库,因此为我提供了可变的列列表。考虑示例:
class TestModel(models.Model):
column_a = models.FloatField()
column_b = models.FloatField()
column_c = models.FloatField()
column_d = models.FloatField()
尝试1:
columns = {'column_a', 'column_c', 'column_d'}
queryset = DummyModel.objects.annotate(total=Sum([F(column_name) for column_name in columns]))
但是,print(queryset.query)
产生错误django.core.exceptions.FieldError: Cannot resolve expression type, unknown output_field
尝试2:
queryset = DummyModel.objects.annotate(total=ExpressionWrapper(Sum([F(column_name) for column_name in columns]), output_field=FloatField()))
这不会产生编译错误,但是SQL查询会产生:
SELECT "test_model"."column_a", "test_model"."column_c", "test_model"."column_d", SUM([]) AS "total" FROM "test_model"
哪个是空的。有谁知道如何解决这个问题?任何帮助将不胜感激!
答案 0 :(得分:1)
要弄清楚这一点,它有助于首先绘制表格:
column_a | column b | column_c
---------+----------+----------
1 | 2 | 3
4 | 5 | 6
7 | 8 | 9
Sum
是“垂直”操作;也就是说,如果我们想要column_a
的总和,我们可以做
>>> DummyModel.objects.aggregate(total=Sum('column_a'))
{'total': 12}
如您所见,它返回1 + 4 + 7 == 12
,因此您可以看到为什么我称其为“垂直”总和。请注意,我们使用aggregate
而不是annotate
:aggregate
用于垂直运算符。
如果相反,我们想要“水平”总和-行中的总和-我们将使用F()
和+
。因此,要在每一行中获取column_a + column_b
,我们将使用
>>> DummyModel.objects.annotate(total=F('column_a') + F('column_b')).values('total')
<QuerySet [{'total': 3}, {'total': 9}, {'total': 15}]>
希望您能看到为什么我将此称为“水平”总和:我们在每一行中分别得到a
和b
的总和。现在请注意,我们使用annotate
,它是用于水平操作的。
如果事先不知道各列的名称,则需要变得棘手并使用functools.reduce和operator.add来构建表达式:
>>> from functools import reduce
>>> from operator import add
>>> cols = ['column_b', 'column_c']
>>> expr = reduce(add, (F(col) for col in cols))
>>> DummyModel.objects.annotate(total=expr).values('total')
<QuerySet [{'total': 5}, {'total': 11}, {'total': 17}]>
如果我们想要水平和垂直的总和(即column_a
的总和加上column_b
的总和-我们需要使用{{1} }和Sum
:
F()
注意:>>> DummyModel.objects.aggregate(total=Sum(F('column_a') + F('column_b')))
{'total': 27}
而不是注释,因为我们最终要进行垂直操作; aggregate
行。是的,首先要进行水平操作,但是由于我们最终Sum
,所以我们需要Sum
。
因此,要总结一下,如果字段是变量,则需要从上方组合aggregate
,aggregate
和Sum
技巧:
reduce
希望这会有所帮助!