Django: ¿La anotación no funciona?

Tengo dos modelos, Producto, que tiene una relación de uno a muchos con un 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 

Si cambio a y b en listas, y concateno, la longitud se resuelve en 210.

¿Alguna idea de lo que está pasando aquí?

Creo que este comportamiento es un error en la asignación de relaciones de objeto de Django. Si miras el SQL que Django genera para tu consulta, verás algo como esto:

 >>> 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 

Tenga en cuenta que la condición de q1 se incluye en la cláusula HAVING de la consulta, pero la condición de q2 se ha perdido.

Puedes solucionar el problema construyendo tu consulta de esta manera:

 >>> 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 

Tenga en cuenta que ambas condiciones ahora están incluidas en la cláusula HAVING .

Le sugiero que informe a los desarrolladores de Django como un error . (Si no puede ser reparado, al menos debería estar documentado).

Los Querysets no admiten la inclusión a nivel de bit. En lugar de generar un error, Django lo trata como un OR lógico y devuelve el primero que evalúa True . Como ambos son querysets válidos, el primero siempre se devuelve.

Si realmente quieres combinar dos consultas, debes convertirlas en listas y luego extender una con la otra, o usar algo como itertools.chain, pero terminarás con un generador que no se puede usar para nada. pero la iteración. De cualquier manera, la combinación de querysets no permitirá ninguna operación adicional en esos querysets.