3

所以我已经阅读了注释列,并利用了 F() 函数,我看到了这篇关于如何对多列求和的帖子。但是,我正在使用 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]))

但是, aprint(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"

哪个是空的。有谁知道如何解决这个问题?任何帮助是极大的赞赏!

4

1 回答 1

5

为了弄清楚这一点,首先对表格进行描绘会有所帮助:

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()and +。所以要进入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.reduceoperator.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——我们需要使用Sumand F()

>>> DummyModel.objects.aggregate(total=Sum(F('column_a') + F('column_b')))
{'total': 27}

注意:aggregate而不是注释,因为我们最终要进行垂直操作;aSum行。是的,首先有一个横向操作,但既然我们最终Sum,我们需要aggregate.

所以,总结一下,如果字段是一个变量,我们需要结合aggregate,Sumreduce上面的诡计:

>>> cols = ['column_b', 'column_c']
>>> expr = reduce(add, (F(col) for col in cols))
>>> DummyModel.objects.aggregate(total=Sum(expr))
{'total': 33}

希望这可以帮助!

于 2018-11-03T15:11:44.500 回答