この動作は、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
fromの条件はクエリの句にq1
含まれていますが、fromの条件は失われていることに注意してください。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
句に含まれていることに注意してください。
これをバグとしてDjango開発者に報告することをお勧めします。(修正できない場合は、少なくとも文書化する必要があります。)