简体   繁体   English

Django:在可变的列数上注释总和

[英]Django: Annotate sum on variable number of columns

So I've read the annotate columns, and utilizing the F() functions, and I saw this post on how to sum multiple columns. 所以,我读过的注释列,并利用F()函数,我看到这个帖子就如何总结多列。 However, I'm working with an EAV style DB, so I'm given a variable list of columns. 但是,我正在使用EAV样式数据库,因此为我提供了可变的列列表。 Consider the example: 考虑示例:

class TestModel(models.Model):
    column_a = models.FloatField()
    column_b = models.FloatField()
    column_c = models.FloatField()
    column_d = models.FloatField()

ATTEMPT 1: 尝试1:

columns = {'column_a', 'column_c', 'column_d'}
queryset = DummyModel.objects.annotate(total=Sum([F(column_name) for column_name in columns]))

However, a print(queryset.query) yields the error django.core.exceptions.FieldError: Cannot resolve expression type, unknown output_field 但是, print(queryset.query)产生错误django.core.exceptions.FieldError: Cannot resolve expression type, unknown output_field

ATTEMPT 2: 尝试2:

queryset = DummyModel.objects.annotate(total=ExpressionWrapper(Sum([F(column_name) for column_name in columns]), output_field=FloatField())) 

This does not yield a compilation error, but the SQL query yields: 这不会产生编译错误,但是SQL查询会产生:

SELECT "test_model"."column_a", "test_model"."column_c", "test_model"."column_d", SUM([]) AS "total" FROM "test_model"

Which is empty. 哪个是空的。 Does anyone have any idea of how to solve this? 有谁知道如何解决这个问题? Any help is greatly appreciated! 任何帮助是极大的赞赏!

To figure this out, it helps to picture the table first: 为了弄清楚这一点,它有助于首先绘制表格:

column_a | column b | column_c
---------+----------+----------
1        | 2        | 3
4        | 5        | 6
7        | 8        | 9

Sum is a "vertical" operation; Sum是“垂直”运算; that is, if we wanted the sum of column_a , we could do 也就是说,如果我们想要column_a的总和,我们可以做

>>> DummyModel.objects.aggregate(total=Sum('column_a'))
{'total': 12}

As you can see, this returns 1 + 4 + 7 == 12 -- so you can see why I call it a "vertical" sum. 如您所见,这将返回1 + 4 + 7 == 12因此您可以看到为什么我称其为“垂直”总和。 Notice we use aggregate rather than annotate : aggregate is for vertical operators. 注意,我们使用aggregate而不是annotateaggregate用于垂直运算符。

If instead we want "horizontal" sum -- the total across a row -- we'd use F() and + . 相反,如果我们想要“水平”总和-行的总和-我们将使用F()+ So to get column_a + column_b in each row, we'd use 因此,要在每一行中获取column_a + column_b ,我们将使用

>>> DummyModel.objects.annotate(total=F('column_a') + F('column_b')).values('total')
<QuerySet [{'total': 3}, {'total': 9}, {'total': 15}]>

Hopefully you can see why I call this a "horizontal" sum: we're getting a sum of a and b "horizontally" in each row. 希望您能明白为什么我称其为“水平”总和:我们在每一行中得到的ab “水平”总和。 And now notice we use annotate , which is for horizontal operations. 现在注意我们使用annotate ,它用于水平操作。

If the names of the columns aren't known beforehand, you'd need to get tricky and use functools.reduce and operator.add to build up an expression: 如果事先不知道各列的名称,则需要花一些技巧并使用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}]>

If we want both a horizontal and a vertical sum -- ie the sum of column_a plus the sum of column_b -- we need to use Sum and F() : 如果我们要同时使用水平和垂直总和-即总和column_a加总和column_b -我们需要用SumF()

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

Notice: aggregate rather than annotate, since we're ultimately going with a vertical operation; 注意: aggregate而不是注释,因为我们最终要进行垂直操作; a Sum of rows. Sum Yes, there's a horizontal operation first, but since we ultimately Sum , we need aggregate . 是的,首先要进行水平操作,但是由于我们最终Sum ,所以需要aggregate

So, to wrap things up, if the fields are a variable, we need to combine aggregate , Sum , and the reduce trickery from above: 因此,要总结一下,如果字段是一个变量,则需要从上面组合aggregateSumreduce技巧:

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

Hope this helps! 希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM