Django 使用大小写进行注释并将注释聚合到分组中时,ORM代码中的KeyError

Django 使用大小写进行注释并将注释聚合到分组中时,ORM代码中的KeyError,django,Django,我们在一个相当复杂的项目中遇到了这个问题,但我已经设法在一个虚拟项目中重现了它: 这是python3.6和postgres10上的django 1.11.7: models.py: tests.py: 运行此测试将导致以下回溯: ====================================================================== ERROR: test_simple (annotate.tests.AnnotateTests) --------------

我们在一个相当复杂的项目中遇到了这个问题,但我已经设法在一个虚拟项目中重现了它:

这是python3.6和postgres10上的django 1.11.7:

models.py: tests.py: 运行此测试将导致以下回溯:

======================================================================
ERROR: test_simple (annotate.tests.AnnotateTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/robin/src/ormweirdness/annotate/tests.py", line 34, in test_simple
    .annotate(amount_sum=Sum(F('percentage') * F('multiplier')))
  File "/Users/robin/.virtualenvs/ormweirdness/lib/python3.6/site-packages/django/db/models/query.py", line 945, in annotate
    clone.query.add_annotation(annotation, alias, is_summary=False)
  File "/Users/robin/.virtualenvs/ormweirdness/lib/python3.6/site-packages/django/db/models/sql/query.py", line 973, in add_annotation
    summarize=is_summary)
  File "/Users/robin/.virtualenvs/ormweirdness/lib/python3.6/site-packages/django/db/models/aggregates.py", line 19, in resolve_expression
    c = super(Aggregate, self).resolve_expression(query, allow_joins, reuse, summarize)
  File "/Users/robin/.virtualenvs/ormweirdness/lib/python3.6/site-packages/django/db/models/expressions.py", line 548, in resolve_expression
    c.source_expressions[pos] = arg.resolve_expression(query, allow_joins, reuse, summarize, for_save)
  File "/Users/robin/.virtualenvs/ormweirdness/lib/python3.6/site-packages/django/db/models/expressions.py", line 412, in resolve_expression
    c.rhs = c.rhs.resolve_expression(query, allow_joins, reuse, summarize, for_save)
  File "/Users/robin/.virtualenvs/ormweirdness/lib/python3.6/site-packages/django/db/models/expressions.py", line 471, in resolve_expression
    return query.resolve_ref(self.name, allow_joins, reuse, summarize)
  File "/Users/robin/.virtualenvs/ormweirdness/lib/python3.6/site-packages/django/db/models/sql/query.py", line 1472, in resolve_ref
    return self.annotation_select[name]
KeyError: 'multiplier'

----------------------------------------------------------------------
我发现那个人似乎也有这个问题。遗憾的是,没有答复


这是怎么回事?

尽管我仍然认为上述方法应该有效(ORM中的bug?),我发现将乘法移到
Case
语句,并在
Sum
语句中直接使用
Case
语句可以停止此问题,因为现在
Sum
不必进行注释查找:

whens = [
    When(multiplier_choice='A', then=F('thing__multiplierA') * F('percentage')),
    When(multiplier_choice='B', then=F('thing__multiplierB') * F('percentage'))
]

multiplier_case = Case(*whens, output_field=FloatField(), default=0)


data_qs = (Data.objects
           # select only certain options to sum up for each thing:
           .filter(thing=thing, option__in=[1, 2])
           .values('thing')
           # group by thing => sum of percentage * multiplier
           .annotate(amount_sum=Sum(multiplier_case))
           .values('amount_sum')
           .order_by('thing'))
这将生成以下SQL:

SELECT SUM(CASE
               WHEN "annotate_data"."multiplier_choice" = A THEN ("annotate_thing"."multiplierA" * "annotate_data"."percentage")
               WHEN "annotate_data"."multiplier_choice" = B THEN ("annotate_thing"."multiplierB" * "annotate_data"."percentage")
               ELSE 0
           END) AS "amount_sum"
FROM "annotate_data"
INNER JOIN "annotate_thing" ON ("annotate_data"."thing_id" = "annotate_thing"."id")
WHERE ("annotate_data"."thing_id" = 1
       AND "annotate_data"."option" IN (1,
                                        2))
GROUP BY "annotate_data"."thing_id"
ORDER BY "annotate_data"."thing_id" ASC
如果有人在每个
中找到一个不意味着重复整个计算(可能比我们正在做的要复杂得多)的解决方案,我将接受你的答案作为解决方案:)

whens = [
    When(multiplier_choice='A', then=F('thing__multiplierA') * F('percentage')),
    When(multiplier_choice='B', then=F('thing__multiplierB') * F('percentage'))
]

multiplier_case = Case(*whens, output_field=FloatField(), default=0)


data_qs = (Data.objects
           # select only certain options to sum up for each thing:
           .filter(thing=thing, option__in=[1, 2])
           .values('thing')
           # group by thing => sum of percentage * multiplier
           .annotate(amount_sum=Sum(multiplier_case))
           .values('amount_sum')
           .order_by('thing'))
SELECT SUM(CASE
               WHEN "annotate_data"."multiplier_choice" = A THEN ("annotate_thing"."multiplierA" * "annotate_data"."percentage")
               WHEN "annotate_data"."multiplier_choice" = B THEN ("annotate_thing"."multiplierB" * "annotate_data"."percentage")
               ELSE 0
           END) AS "amount_sum"
FROM "annotate_data"
INNER JOIN "annotate_thing" ON ("annotate_data"."thing_id" = "annotate_thing"."id")
WHERE ("annotate_data"."thing_id" = 1
       AND "annotate_data"."option" IN (1,
                                        2))
GROUP BY "annotate_data"."thing_id"
ORDER BY "annotate_data"."thing_id" ASC