7

做我的第一個真正的Django項目,並且需要指導。在Django中註釋導致'None'值的SUM聚合函數

背景: 我的項目是一個reddit克隆。用戶提交鏈接+文字。訪問者upvote或downvote。有一個社交排名算法,每運行約2分鐘作爲背景腳本,根據網絡投票和內容新鮮度重新排列所有提交內容。相當香草的東西。

問題: 排序方式votes無法正常工作,因爲votes被初始化爲None,而不是0。這會導致None投票的投稿數量低於提交的反對投票數。我已經調整了這個問題幾天 - 沒有運氣。

具體細節: 我過我纏身模型的模型管理器來標註Sum聚合函數查詢集,然後責令該查詢通過「社會地位」和投票設置。下面是我的models.py。我使用Django 1.5,這樣一些東西,你看到這裏可能不符合1.8(如get_query_set VS 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,而不是去爲解決中指出, (!)在Django的bug跟蹤票開了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

,你現在可以刪除if語句。

與其他許多語言一樣,它返回非falsy值(None,0,「」)或最後一個值'0'。