0

我有两个模型,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。

知道这里发生了什么吗?

4

2 回答 2

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

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

您可以通过像这样构建查询来解决该问题:

>>> 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子句中。

我建议您将此作为错误报告给 Django 开发人员。(如果无法修复,那么至少应该记录在案。)

于 2012-09-27T23:26:16.527 回答
-1

查询集不支持按位包含。Django 没有引发错误,而是将其视为逻辑 OR 并返回第一个计算True. 因为它们都是有效的查询集,所以总是返回第一个。

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

于 2012-09-27T20:06:30.160 回答