UPDATE个
多亏了贴出的答案,我找到了一个更简单的方法来解决这个问题.原始问题可以在修订历史中看到.
问题
我正在try 将SQL查询转换为Django,但收到一个我不理解的错误.
这是我的Django 模型:
class Title(models.Model):
title_id = models.CharField(primary_key=True, max_length=12)
title = models.CharField(max_length=80)
publisher = models.CharField(max_length=100)
price = models.DecimalField(decimal_places=2, blank=True, null=True)
我有以下数据:
publisher title_id price title
--------------------------- ---------- ------- -----------------------------------
New Age Books PS2106 7 Life Without Fear
New Age Books PS2091 10.95 Is Anger the Enemy?
New Age Books BU2075 2.99 You Can Combat Computer Stress!
New Age Books TC7777 14.99 Sushi, Anyone?
Binnet & Hardley MC3021 2.99 The Gourmet Microwave
Binnet & Hardley MC2222 19.99 Silicon Valley Gastronomic Treats
算法data Infosystems PC1035 22.95 But Is It User Friendly?
算法data Infosystems BU1032 19.99 The Busy Executive's Database Guide
算法data Infosystems PC8888 20 Secrets of Silicon Valley
下面是我想要做的:引入一个价格两倍的带注释的字段dbl_price
,然后用publisher
对结果查询集进行分组,并 for each 出版商计算该出版商出版的所有图书的全部dbl_price
个值的总和.
执行此操作的SQL查询如下所示:
SELECT SUM(dbl_price) AS total_dbl_price, publisher
FROM (
SELECT price * 2 AS dbl_price, publisher
FROM title
) AS A
GROUP BY publisher
所需的输出将为:
publisher tot_dbl_prices
--------------------------- --------------
算法data Infosystems 125.88
Binnet & Hardley 45.96
New Age Books 71.86
Django查询
查询如下所示:
Title.objects
.annotate(dbl_price=2*F('price'))
.values('publisher')
.annotate(tot_dbl_prices=Sum('dbl_price'))
但会给出一个错误:
KeyError: 'dbl_price'.
这表明它在查询集中找不到字段dbl_price
.
错误的原因
以下是发生此错误的原因:the documentation says
您还应注意,Average_Rating已明确包括在内 在要返回的值列表中.这是必需的,因为VALUES()和ANNOTATE()子句的顺序.
如果VALUES()子句在ANNOTATE()子句之前,则所有批注 将自动添加到结果集中.但是,如果 如果在ANNOTATE()子句之后应用VALUES()子句,则需要显式包含聚合列.
因此,在聚合中找不到dbl_price
,因为它是由先前的annotate
创建的,但不包括在values()
中.
但是,我也不能将其包含在values
中,因为我想使用values
(后面跟着另一个annotate
)作为分组设备,因为
如果values()子句位于annotate()之前,则将使用values()子句描述的分组来计算注释.
这就是Django implements SQL GROUP BY
号的基础.这意味着我不能将dbl_price
包含在values()
中,因为这样分组将基于字段publisher
和dbl_price
的唯一组合,而我只需要按publisher
分组.
因此,下面的查询实际上是有效的,它与上面的不同之处在于我对模型的price
字段进行了聚合,而不是对dbl_price
字段进行了注释:
Title.objects
.annotate(dbl_price=2*F('price'))
.values('publisher')
.annotate(sum_of_prices=Count('price'))
因为price
字段在模型中,而不是一个带注释的字段,所以我们不需要将其包含在values
中以将其保留在queryset中.
问题是
因此,现在我们有了它:我需要将带注释的属性包括到values
中以将其保留在查询集中,但我不能这样做,因为values
也用于分组(如果使用额外的字段,这将是错误的).问题本质上是由于Django中使用values
的两种截然不同的方式(values
后面是否跟annotate
)-即(1)值提取(SQL普通SELECT
列表)和(2)分组+组上的聚合(SQL GROUP BY
)-在这种情况下,这两种方式似乎是冲突的.
My question is:有没有办法解决这个问题(不用退回到原始SQL)?
Please note:有问题的具体例子可以通过将所有annotate
条语句移到values
之后来解决,其中有几个答案指出了这一点.然而,我更感兴趣的解决方案(或讨论)将保持annotate
个声明之前values()
个,原因有三:1.还有一些更复杂的例子,建议的解决方案不起作用.2.我可以想象这样的情况,带注释的queryset被传递给另一个函数,该函数实际上是分组的,因此我们只知道带注释字段的名称集及其类型.3.情况似乎很简单,如果之前没有人注意到和讨论values()
两种不同用法的冲突,我会感到惊讶.