Django:注释不起作用?

时间:2012-09-27 19:51:36

标签: python django annotations

我有两个模型,Product,与RatingEntry有一对多的关系:

>>> product_entries = models.Products.objects.all()
>>> annotated = product_entries.annotate(Count("ratingentry"))
>>> len(annotated)
210
>>> a = annotated.filter(ratingentry__count__lte = 10)
>>> b = annotated.filter(ratingentry__count__gt = 10)
>>> len(a)
10
>>> len(b)
200
>>> len(a | b)
10 //should be 210

如果我将a和b更改为列表并连接它们,则长度可以达到210。

知道这里发生了什么吗?

2 个答案:

答案 0 :(得分:2)

我认为这种行为是Django对象关系映射中的一个错误。如果你看一下Django为你的查询生成的SQL,那么你会看到类似这样的东西:

>>> q1 = (Products.objects.annotate(num_ratings = Count('ratingentries'))
...       .filter(num_ratings__gt = 10))
>>> q2 = (Products.objects.annotate(num_ratings = Count('ratingentries'))
...       .exclude(num_ratings__gt = 10))
>>> print(str((q1 | q2).query))
SELECT `myapp_products`.`id`, COUNT(`myapp_ratingentries`.`id`) AS
`num_ratings` FROM `myapp_products` LEFT OUTER JOIN `myapp_ratingentries` ON
(`myapp_products`.`id` = `myapp_ratingentries`.`product_id`) GROUP BY
`myapp_products`.`id` HAVING COUNT(`myapp_ratingentries`.`id`) > 10
ORDER BY NULL

请注意q1中的条件包含在查询的HAVING子句中,但q2中的条件已丢失。

您可以通过以下方式构建查询来解决此问题:

>>> q = Q(num_products__gt = 10) | ~Q(num_products__gt = 10)
>>> q3 = Products.objects.annotate(num_ratings = Count('ratingentries')).filter(q)
>>> print(str(q3.query))
SELECT `myapp_products`.`id`, COUNT(`myapp_ratingentries`.`id`) AS
`num_ratings` FROM `myapp_products` LEFT OUTER JOIN `myapp_ratingentries` ON
(`myapp_products`.`id` = `myapp_ratingentries`.`product_id`) GROUP BY
`myapp_products`.`id` HAVING (COUNT(`myapp_ratingentries`.`id`) > 10 OR NOT
(COUNT(`myapp_ratingentries`.`id`) > 10 )) ORDER BY NULL

请注意,这两个条件现在都包含在HAVING子句中。

我建议你report this to the Django developers as a bug。 (如果无法修复,那么至少应记录下来。)

答案 1 :(得分:-1)

Querysets不支持按位包含。 Django将其视为逻辑OR并返回评估True的第一个,而不是引发错误。由于它们都是有效的查询集,因此始终返回第一个。

如果你想实际组合两个查询集,你需要将它们转换为列表然后用另一个扩展一个,或者使用像itertools.chain这样的东西,但你最终会得到一个不能生成的生成器用于除迭代之外的任何事情。无论哪种方式,组合查询集都将禁止对这些查询集进行任何进一步操作。