Аннотирование суммы приводит к нулю, а не нулю
Я делаю сайт QA, похожий на страницу, на которой вы сейчас находитесь. Я пытаюсь заказать ответы по их оценке, но ответы, у которых нет голосов, имеют свой балл, равный None, а не 0. Это приводит к ответам, в которых нет голосов, находящихся внизу страницы ниже, с отрицательными оценками. Как я могу сделать аннотированный счет равным нулю, если нет ответа на вопрос?
Здесь моя модель:
from django.contrib.auth.models import User
Answer(models.Model):
//some fields here
pass
VOTE_CHOICES = ((-1, Down), (1, Up))
Vote(models.Model):
user = models.ForeignKey(User)
answer = models.ForeignKey(Answer)
type = models.IntegerField(choices = VOTE_CHOICES)
class Meta:
unique_together = (user, answer)
И вот мой запрос:
answers = Answer.objects.filter(<something here>)
.annotate(score=Sum('vote__type'))
.order_by('-score')
edit: И чтобы быть ясным, я хотел бы сделать это в запросе. Я знаю, что могу превратить его в список, а затем отсортировать его в своем коде на Python, но я бы хотел, если это возможно, избежать этого.
Ответы
Ответ 1
Вы можете использовать Coalesce
функцию из django.db.models.functions
, например:
answers = Answer.objects.filter(<something here>)
.annotate(score=Coalesce(Sum('vote__type'), 0))
.order_by('-score')
Ответ 2
Вы можете использовать это обходное решение, но я не уверен, насколько это совместимо с различными базами данных (однако работает в sqlite3):
Answer.objects.extra(select={
'score': 'IFNULL((SELECT SUM(type) ' + \
'FROM <yourapp>_vote ' + \
'WHERE <yourapp>_vote.answer_id = ' + \
'<yourapp>_answer.id), ' + \
'0)'
}).order_by('-score')
Ответ 3
Как насчет использования пользовательского Manager
? Например:
AnswerManager(models.Manager):
def all_with_score(self):
qs = self.get_query_set().annotate(score=Sum('vote__type'))
# Here, you can do stuff with QuerySet, for example
# iterate over all Answers and set 'score' to zero if None.
Answer(models.Model):
//some fields here
objects = AnswerManager()
Затем вы можете использовать:
>>> answers = Answer.objects.all_with_score().order_by('-score')