注释 SUM 聚合函数导致 Django 中的“无”值

Posted

技术标签:

【中文标题】注释 SUM 聚合函数导致 Django 中的“无”值【英文标题】:Annotating SUM aggregation function leading to 'None' value in Django 【发布时间】:2015-11-25 09:16:46 【问题描述】:

做我的第一个真正的 Django 项目,需要指导。

背景: 我的项目是一个 reddit 克隆。用户提交链接+文本。访客赞成或反对。有一个 social_ranking 算法,每 2 分钟运行一次,作为后台脚本,根据网络投票和内容的新鲜度重新排列所有提交的内容。相当香草的东西。

问题: 通过votes 订购无法正常工作,因为votes 被初始化为None 而不是0。这会导致None 投票的提交排名低于投票反对的提交。我已经调试这个问题好几天了 - 没有运气。

具体说明: 我已经覆盖了我的模型的模型管理器,将 Sum 聚合函数注释到查询集,然后按“社会等级”和投票对所述查询集进行排序。

下面是我的 models.py。我正在使用Django 1.5,因此您在此处看到的某些内容可能与 1.8 不对应(例如 get_query_setget_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”问题?提前致谢。

【问题讨论】:

如果设置 null=False 保持默认值=0 会怎样? 【参考方案1】:

虽然我选择忽略 None 条目,将它们排除在结果之外,但还是碰上了同一堵墙。猜你不想这样。

顺便说一句,这个问题有同样的问题Annotating a Sum results in None rather than zero

至于除了使用该问题的答案中指出的自定义 sql 之外的解决方案,您可以改用 Django 1.8 并选择在 Django 的错误跟踪器中打开超过 6 年的票中指出的解决方案(!)@ 987654322@

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。

【讨论】:

【参考方案2】:

你也可以换行

netvotes = self.votes

netvotes = self.votes or 0

您现在可以删除 if 语句。

这在许多其他语言中的作用是返回非 falsy 值(None, 0, ""),或者在这种特殊情况下返回最后一个值 '0'。

【讨论】:

以上是关于注释 SUM 聚合函数导致 Django 中的“无”值的主要内容,如果未能解决你的问题,请参考以下文章

注释总和的 Django 聚合平均值(1.6.5)

Django(18)聚合函数

075: Django数据库ORM聚合函数详解-Sum

sql 聚合函数都有哪些

Django学习路17_聚合函数(Avg平均值,Count数量,Max最大,Min最小,Sum求和)基本使用

Django学习路17_聚合函数(Avg平均值,Count数量,Max最大,Min最小,Sum求和)基本使用