【问题标题】:Django: annote through several multi-valued relationshipsDjango:通过几个多值关系进行注释
【发布时间】:2016-06-17 19:13:12
【问题描述】:

Django 1.8 和 1.9,Postgres 数据库。

我有一个Contest 模型,它可以包含Users 提交的许多相关Entry 对象。

class Contest(models.Model):
    title = models.CharField(max_length=50)

class Entry(models.Model):
    contest = models.ForeignKey(Contest, related_name="entries")
    user = models.ForeignKey(settings.AUTH_USER_MODEL)

我想获取所有Contest 对象的列表,并用complete 标志进行注释,指示特定user 是否为每个Contest 提交了Entry

from django.db.models import Case, When, BooleanField, Value

user = User.objects.get(...)
contests = Contest.objects.annotate(
    complete=Case(
        When(entries__user=user, then=Value(True)),
        default=Value(False),
        output_field=BooleanField(),
    )
)

contests 上的预期结果将是这样的(对于 user1):

但是,我得到的重复元素取决于每个 Contest 上的 Entries 数量(注意竞赛 4 和 5):

此时,我尝试将distinct() 添加到查询集的末尾,但由于考虑到新的complete 注释,我仍然会收到Contest 4 的重复项。

由于我使用 Django >= 1.8 并使用 Postgres,因此我想到了使用 distinct("id")。可悲的是,这在Contest 4 中设置为False

这就是我决定发布这个问题的部分。关于如何从当前结果到预期结果(第一个表)的任何指示?通过使用条件注释,我是否大大忽略了某些东西?

【问题讨论】:

    标签: python django postgresql django-queryset django-orm


    【解决方案1】:

    您可以同时添加order_bydistinct 以获得您想要的结果。

    contests = Contest.objects.annotate(
        complete=Case(
            When(entries__user=user, then=Value(True)),
            default=Value(False),
            output_field=BooleanField(),
        )
    ).order_by('id', '-complete').distinct('id')
    

    请注意distinct文档中的以下注释

    指定字段名时,必须在QuerySet中提供order_by()order_by()中的字段必须以distinct()中的字段开头,顺序相同。

    例如,SELECT DISTINCT ON (a) 为您提供column a 中每个值的第一行。如果你不指定顺序,你会得到一些任意的行。

    所以首先按id 对结果进行排序,然后按complete 的降序排列(否则你会得到complete=False在顶部的行)。并且,然后使用 distinct('id') 保留每个 id 的第一行并删除其他行。

    【讨论】:

    • 非常感谢,这会产生预期的结果。巧合的是,我确实需要按另一个字段排序比赛(示例中未显示,假设它是一个日期)。我想我可以通过将查询集转换为列表并在那里进行排序来摆脱它。
    猜你喜欢
    • 2020-01-30
    • 2012-03-20
    • 2021-06-24
    • 1970-01-01
    • 2011-03-23
    • 2021-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多