【问题标题】:Django: Annotate sum on variable number of columnsDjango:在可变数量的列上注释总和
【发布时间】:2019-04-07 11:26:34
【问题描述】:

所以我已经阅读了注释列,并使用了 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"

这是空的。有谁知道如何解决这个问题?非常感谢任何帮助!

【问题讨论】:

    标签: python django django-annotate


    【解决方案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 而不是annotateaggregate 用于垂直运算符。

    如果我们想要“水平”总和——一行的总和——我们会使用F()+。所以要在每一行中获得column_a + column_b,我们会使用

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

    希望您能明白我为什么将其称为“水平”总和:我们在每一行中得到 ab “水平”的总和。现在注意我们使用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的总和——我们需要使用SumF()

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

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

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

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

    希望这会有所帮助!

    【讨论】:

    • 是的,这就是我要找的。我实际上可以更简单,甚至可以合法地使用 python 中的简单sumF() 进行水平求和以使其正常工作。使用reduce 会更通用,但对于我的使用目的来说不是必需的。无论如何,我不需要聚合,它只是需要一个动态的水平求和。原来我很接近。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2021-11-02
    • 2016-10-06
    • 1970-01-01
    • 2018-03-18
    • 2020-08-24
    • 1970-01-01
    • 2016-01-30
    • 2013-02-28
    相关资源
    最近更新 更多