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

さて、問題について

この後、ポイントのない人は、値が0 ではなく にpoints変わります。None

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

なぜこれが起こるのか誰にも分かりますか?

ありがとう!

4

1 に答える 1

1

次のように、クエリセットに再度注釈を付けて null 値を 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 クエリをチェックしてこの状況をさらに調査するように指示されたところ、次のことがわかりました。

これは 2 つのクエリの結合であるため、2 番目のクエリ アノテーションは何らかの理由で考慮されず、COALESCEto 0 は使用されません。それを最初のクエリに移動すると、2 番目のクエリに伝播され、期待される結果が得られます。

基本的に、次のように変更しました。

# 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 に答える