Django:通过几个多值关系进行注释

Django: annote through several multi-valued relationships

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.

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 的预期结果是这样的(对于用户 1):

但是,我得到了重复的元素,具体取决于每个 ContestEntries 的数量(注意比赛 4 和 5):

此时,我尝试将 distinct() 添加到查询集的末尾,但由于考虑了新的 complete 注释,我仍然得到 [=25= 的重复项].

由于我使用的是 Django >= 1.8 并使用 Postgres,所以我想到了使用 distinct("id")。遗憾的是,这在 Contest 4.

中设置为 False

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

您可以同时添加 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

文档中的以下注释

When you specify field names, you must provide an order_by() in the QuerySet, and the fields in order_by() must start with the fields in distinct(), in the same order.

For example, SELECT DISTINCT ON (a) gives you the first row for each value in column a. If you don’t specify an order, you’ll get some arbitrary row.

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