Django:在可变列数上注释总和
Django: Annotate sum on variable number of columns
所以我阅读了注释列,并利用了 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]))
但是,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"
里面是空的。有谁知道如何解决这个问题?非常感谢任何帮助!
要弄清楚这一点,先想象一下 table 会有所帮助:
column_a | column b | column_c
---------+----------+----------
1 | 2 | 3
4 | 5 | 6
7 | 8 | 9
Sum
是一个"vertical"操作;也就是说,如果我们想要 column_a
的总和,我们可以做
>>> DummyModel.objects.aggregate(total=Sum('column_a'))
{'total': 12}
如您所见,这个 returns 1 + 4 + 7 == 12
-- 所以您会明白为什么我称它为 "vertical" 总和。请注意,我们使用 aggregate
而不是 annotate
:aggregate
用于垂直运算符。
如果我们想要 "horizontal" 总和——一行的总数——我们会使用 F()
和 +
。因此,要在每一行中获得 column_a + column_b
,我们将使用
>>> DummyModel.objects.annotate(total=F('column_a') + F('column_b')).values('total')
<QuerySet [{'total': 3}, {'total': 9}, {'total': 15}]>
希望您能明白我为什么称其为 "horizontal" 总和:我们在每一行中得到 a
和 b
"horizontally" 的总和。现在注意我们使用 annotate
,它用于水平操作。
如果事先不知道列的名称,您需要使用技巧并使用 functools.reduce and 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
的总和——我们需要使用 Sum
和 F()
:
>>> DummyModel.objects.aggregate(total=Sum(F('column_a') + F('column_b')))
{'total': 27}
注意:aggregate
而不是注释,因为我们最终要进行垂直操作; Sum
行。是的,首先是横向操作,但由于我们最终Sum
,所以我们需要aggregate
。
所以,总结一下,如果字段是变量,我们需要结合 aggregate
、Sum
和上面的 reduce
技巧:
>>> cols = ['column_b', 'column_c']
>>> expr = reduce(add, (F(col) for col in cols))
>>> DummyModel.objects.aggregate(total=Sum(expr))
{'total': 33}
希望对您有所帮助!
所以我阅读了注释列,并利用了 F() 函数,并且我看到了
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"
里面是空的。有谁知道如何解决这个问题?非常感谢任何帮助!
要弄清楚这一点,先想象一下 table 会有所帮助:
column_a | column b | column_c
---------+----------+----------
1 | 2 | 3
4 | 5 | 6
7 | 8 | 9
Sum
是一个"vertical"操作;也就是说,如果我们想要 column_a
的总和,我们可以做
>>> DummyModel.objects.aggregate(total=Sum('column_a'))
{'total': 12}
如您所见,这个 returns 1 + 4 + 7 == 12
-- 所以您会明白为什么我称它为 "vertical" 总和。请注意,我们使用 aggregate
而不是 annotate
:aggregate
用于垂直运算符。
如果我们想要 "horizontal" 总和——一行的总数——我们会使用 F()
和 +
。因此,要在每一行中获得 column_a + column_b
,我们将使用
>>> DummyModel.objects.annotate(total=F('column_a') + F('column_b')).values('total')
<QuerySet [{'total': 3}, {'total': 9}, {'total': 15}]>
希望您能明白我为什么称其为 "horizontal" 总和:我们在每一行中得到 a
和 b
"horizontally" 的总和。现在注意我们使用 annotate
,它用于水平操作。
如果事先不知道列的名称,您需要使用技巧并使用 functools.reduce and 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
的总和——我们需要使用 Sum
和 F()
:
>>> DummyModel.objects.aggregate(total=Sum(F('column_a') + F('column_b')))
{'total': 27}
注意:aggregate
而不是注释,因为我们最终要进行垂直操作; Sum
行。是的,首先是横向操作,但由于我们最终Sum
,所以我们需要aggregate
。
所以,总结一下,如果字段是变量,我们需要结合 aggregate
、Sum
和上面的 reduce
技巧:
>>> cols = ['column_b', 'column_c']
>>> expr = reduce(add, (F(col) for col in cols))
>>> DummyModel.objects.aggregate(total=Sum(expr))
{'total': 33}
希望对您有所帮助!