Django annotate возвращает дублирующиеся записи
Я аннотирую набор запросов следующим образом:
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)
),
)
В данном примере SubItem
является моделью с внешним ключом к Item
.
При выполнении этого кода происходит странное поведение. Предположим, у нас есть 1 Item
и 2 SubItem
, связанных с ним. У одного подпункта статус отправлен, а у другого - нет. Когда я запускаю annotate на queryset, queryset возвращает элемент дважды, один раз с has_sent_subitems
, установленным в True
, а другой - в False
. Другая странная вещь заключается в том, что один дубликат имеет count_subitems == 1
, а другой count_subitems == 1
, как будто queryset разделил элемент на две строки, одна из которых status == 'sent'
, а другая status != 'sent'
.
Вот как выглядит аннотированный набор запросов:
[
{
'name': 'Item Name',
'count_subitems': 1,
'has_sent_subitem': False
},
{
'name': 'Item Name',
'count_subitems': 1,
'has_sent_subitem': True
}
]
Вот как выглядит база данных, используя псевдокод:
item = Item()
SubItem(item=item, status=draft)
SubItem(item=item, status=sent)
Я почти уверен, что это связано со строкой When(subitems__status=Status.sent, then=Value(True)),
. Есть ли способ сделать так, чтобы эта строка проверяла, что только 1 элемент имеет статус отправлен, а затем устанавливала аннотацию в true и двигалась дальше?
P.S. Использование .distinct()
не сработало. Я не могу использовать .distinct(field)
, потому что annotate() + distinct(fields) is not implemented.
Вы можете использовать для этого подзапрос Exists
, чтобы избежать объединения, вызванного subitems__status
, так:
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)
),
)