#django #django-models #django-1.10
#django #django-модели #django-1.10
Вопрос:
Я хочу объединить сумму связанных значений от пользователей с пользователями, у которых нет этих значений.
Вот упрощенная версия структуры моей модели:
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}]>
Кто-нибудь имеет представление о том, почему это происходит?
Спасибо!
Ответ №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
значение 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