7

Выполнение моего первого реального проекта Django и руководство.Аннотирование функции агрегации SUM, приводящей к значению «Нет» в Django

Мой проект - красноватый клон. Пользователи отправляют ссылки + текст. Посетители остаются в стороне или вниз. Существует social_ranking algo, выполняется каждые ~ 2 минуты в качестве фонового скрипта, переписывает все материалы в соответствии с чистым голосом и свежестью контента. Довольно ванильный материал.

Проблема: Сортировка по votes не работает правильно, потому что votes выполняется инициализация, как None вместо 0. Это приводит к сообщениям с None голосами за ранг ниже заявок с отрицательными голосами. Я отлаживал эту проблему в течение нескольких дней - не повезло.

Особенности: Я подавляться менеджер модели моей модели аннотировать функцию Sum агрегации для множества запросов, а затем заказать указанный запрос, установленный «социального ранга» и голосов.

Под моим models.py. Я использую Django 1.5, таким образом, некоторые вещи, которые Вы видите здесь могут не соответствовать 1.8 (например get_query_set против get_queryset):

class LinkVoteCountManager(models.Manager): 
    def get_query_set(self): 
     return super(LinkVoteCountManager, self).get_query_set().annotate(votes=Sum('vote__value')).order_by('-rank_score', '-votes') 

class Link(models.Model): 
    description = models.TextField(_("Write something")) 
    submitter = models.ForeignKey(User) 
    submitted_on = models.DateTimeField(auto_now_add=True) 
    rank_score = models.FloatField(default=0.0) 
    url = models.URLField(_("Link"), max_length=250, blank=True) 

    with_votes = LinkVoteCountManager() 
    objects = models.Manager() 

    def __unicode__(self): 
     return self.description 

    def set_rank(self): 
     # Based on reddit ranking algo at http://amix.dk/blog/post/19588 
     epoch = datetime(1970, 1, 1).replace(tzinfo=None) 
     netvotes = self.votes # 'NONE' votes are messing up netvotes amount. 
     if netvotes == None: 
      netvotes = 0 
     order = log(max(abs(netvotes), 1), 10) 
     sign = 1 if netvotes > 0 else -1 if netvotes < 0 else 0 
     unaware_submission = self.submitted_on.replace(tzinfo=None) 
     td = unaware_submission - epoch 
     epoch_submission = td.days * 86400 + td.seconds + (float(td.microseconds)/1000000) 
     secs = epoch_submission - 1432201843 
     self.rank_score = round(sign * order + secs/45000, 8) 
     self.save() 

class Vote(models.Model): 
    voter = models.ForeignKey(User) 
    link = models.ForeignKey(Link) 
    value = models.IntegerField(null=True, blank=True, default=0) 

    def __unicode__(self): 
     return "%s gave %s to %s" % (self.voter.username, self.value, self.link.description) 

При необходимости, следующие соответствующие разделы от моего views.py:

class LinkListView(ListView): 
    model = Link 
    queryset = Link.with_votes.all() 
    paginate_by = 10 

    def get_context_data(self, **kwargs): 
     context = super(LinkListView, self).get_context_data(**kwargs) 
     if self.request.user.is_authenticated(): 
      voted = Vote.objects.filter(voter=self.request.user) 
      links_in_page = [link.id for link in context["object_list"]] 
      voted = voted.filter(link_id__in=links_in_page) 
      voted = voted.values_list('link_id', flat=True) 
      context["voted"] = voted 
     return context 

class LinkCreateView(CreateView): 
    model = Link 
    form_class = LinkForm 

    def form_valid(self, form): 
     f = form.save(commit=False) 
     f.rank_score=0 
     f.with_votes = 0 
     f.category = '1' 
     f.save() 
     return super(CreateView, self).form_valid(form) 

Может ли кто-нибудь пролить свет на то, что мне нужно сделать, чтобы исправить проблему «None»? Заранее спасибо.

+0

что делать, если вы установили значение null = False сохранение значения по умолчанию = 0? –

ответ

11

Просто нажмите на ту же стену, хотя я решил проигнорировать None записей, исключив их из результатов. Думаю, вы этого не хотите.

Кстати, этот вопрос имеет и тот же вопрос Annotating a Sum results in None rather than zero

Как для решения других, чем с помощью пользовательского SQL, как указано в ответе на этот вопрос, вы можете использовать Django 1.8, а и идти для решения отметил в (!) открытый билет в багтрекер Джанго более 6 лет https://code.djangoproject.com/ticket/10929

Coalesce(Sum('field'), 0) 

Таким образом, ваш менеджер будет:

class LinkVoteCountManager(models.Manager): 
    def get_query_set(self): 
     return super(LinkVoteCountManager, self).get_query_set().annotate(
      votes=Coalesce(Sum('vote__value'), 0) 
     ).order_by(
      '-rank_score', 
      '-votes' 
     ) 

PS: Я не тестировал код, так как сам я не использую Django 1.8.

+0

Спасибо за этого человека! –

+0

@HassanBaig Рад, что смогу помочь – alfetopito

1

Вы также можете заменить строку

netvotes = self.votes

в

netvotes = self.votes or 0

и теперь вы можете удалить, если заявление.

Как это делается на многих других языках, необходимо вернуть значение falsy (None, 0, "") или последнее значение '0' в данном конкретном случае.