0

我想将来自用户的相关值的总和与没有这些值的用户连接起来。

这是我的模型结构的简化版本:

class Answer(models.Model):
    person = models.ForeignKey(Person)
    points = models.PositiveIntegerField(default=100)
    correct = models.BooleanField(default=False)

class Person(models.Model):
    # irrelevant model fields

样本数据集:

Person | Answer.Points
------ | ------
3      | 50
3      | 100
2      | 100
2      | 90

Person 4 has no answers and therefore, points

通过下面的查询,我可以实现每个人的积分总和:

people_with_points = Person.objects.\
        filter(answer__correct=True).\
        annotate(points=Sum('answer__points')).\
        values('pk', 'points')

<QuerySet [{'pk': 2, 'points': 190}, {'pk': 3, 'points': 150}]>

但是,由于有些人可能没有任何相关Answer条目,他们将获得 0 分,并且我使用下面的查询Coalesce来“伪造”他们的积分,如下所示:

people_without_points = Person.objects.\
        exclude(pk__in=people_with_points.values_list('pk')).\
        annotate(points=Coalesce(Sum('answer__points'), 0)).\
        values('pk', 'points')

<QuerySet [{'pk': 4, 'points': 0}]>

这两个都按预期工作,但我想让它们在同一个查询集中,所以我使用联合运算符|加入它们:

everyone = people_with_points | people_without_points

现在,对于问题

在此之后,没有积分的人的points价值变成了None而不是0。

<QuerySet [{'pk': 2, 'points': 190}, {'pk': 3, 'points': 150}, {'pk': 4, 'points': None}]>

任何人都知道为什么会发生这种情况?

谢谢!

4

1 回答 1

1

我应该提到,我可以通过再次注释查询集并将空值合并为 0 来解决这个问题,如下所示:

everyone.\
    annotate(real_points=Concat(Coalesce(F('points'), 0), Value(''))).\
    values('pk', 'real_points')

<QuerySet [{'pk': 2, 'real_points': 190}, {'pk': 3, 'real_points': 150}, {'pk': 4, 'real_points': 0}]>

但我想了解为什么工会不像我在最初的问题中所期望的那样工作。

编辑:我想我明白了。一位朋友指示我使用django-debug-toolbar检查我的 SQL 查询以进一步调查这种情况,我发现了以下内容:

由于它是两个查询的联合,因此不考虑第二个查询注释并且COALESCE不使用 to 0。通过将其移动到第一个查询,它被传播到第二个查询,我可以达到预期的结果。

基本上,我更改了以下内容:

# Moved the "Coalesce" to the initial query
people_with_points = Person.objects.\
    filter(answer__correct=True).\
    annotate(points=Coalesce(Sum('answer__points'), 0)).\
    values('pk', 'points')

# Second query does not have it anymore
people_without_points = Person.objects.\
    exclude(pk__in=people_with_points.values_list('pk')).\
    values('pk', 'points')

# We will have the values with 0 here!
everyone = people_with_points | people_without_points
于 2016-10-19T13:50:00.917 回答