Django 注释返回重复条目

Django annotate returning duplicate entries

我正在像这样注释查询集:

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 链接到它。一个子项的状态已发送,而另一个没有。当我 运行 在查询集上注释时,查询集 returns 项目两次,一次 has_sent_subitems 设置为 True,另一次设置为 False。另一个奇怪的事情是,一个副本有 count_subitems == 1,另一个有 count_subitems == 1,就好像查询集将项目分成两行,一个在 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)
            ),
        )