Estoy anotando un conjunto de consultas así:
class ItemQuerySet(models.QuerySet): def annotate_subitem_stats(self): return self.annotate( count_subitems=Count('subitems'), has_sent_subitems=Case( When(subitems__status=Status.sent, then=Value(True)), default=Value(False) ), ) En este ejemplo, SubItem es un modelo con una clave externa para Item .
Ocurre un comportamiento extraño cuando ejecuto este código. Supongamos que tenemos 1 Item y 2 SubItem vinculados a él. Un subelemento tiene estado enviado y el otro no. Cuando ejecuto anotar en el conjunto de consultas, el conjunto de consultas devuelve el elemento dos veces, uno con has_sent_subitems establecido en True y el otro establecido en False . La otra cosa extraña es que un duplicado tiene count_subitems == 1 , y el otro tiene count_subitems == 1 , como si el conjunto de consultas hubiera dividido el elemento en dos filas, una donde status == 'sent' y la otra donde status != 'sent' .
Básicamente, así es como se ve el conjunto de consultas anotado:
[ { 'name': 'Item Name', 'count_subitems': 1, 'has_sent_subitem': False }, { 'name': 'Item Name', 'count_subitems': 1, 'has_sent_subitem': True } ]Así es como se ve la base de datos, usando pseudocódigo:
item = Item() SubItem(item=item, status=draft) SubItem(item=item, status=sent) Estoy bastante seguro de que esto tiene que ver con la línea When(subitems__status=Status.sent, then=Value(True)), . ¿Hay alguna manera de que pueda hacer que esa línea verifique si solo 1 elemento tiene el estado enviado, y luego establecer la anotación en verdadero y continuar?
PD Usar .distinct() no funcionó. No puedo usar .distinct(field) , porque annotate() + distinct(fields) is not implemented.
En su lugar, puede usar la subconsulta Exists para esto, para evitar la combinación causada por subitems__status , entonces:
from django.db.models import Exists, OuterRef class ItemQuerySet(models.QuerySet): def annotate_subitem_stats(self): return self.annotate( count_subitems=Count('subitems'), has_sent_subitems=Exists( SubItem.objects.filter(item=OuterRef('pk'), status=Status.sent) ), )