按聚合字段值排序的查询集

28

假设我有以下模型:

class Contest:
    title = models.CharField( max_length = 200 )
    description = models.TextField()

class Image:
    title = models.CharField( max_length = 200 )
    description = models.TextField()
    contest = models.ForeignKey( Contest )
    user = models.ForeignKey( User )

    def score( self ):
        return self.vote_set.all().aggregate( models.Sum( 'value' ) )[ 'value__sum' ]

class Vote:
    value = models.SmallIntegerField()
    user = models.ForeignKey( User )
    image = models.ForeignKey( Image )

网站的用户可以将他们的图片上传到多个比赛中,其他用户可以对它们进行投票。

一切正常,但现在我想展示一个页面,让用户可以看到某个比赛的所有贡献。这些图片应按它们的得分排序。

因此,我尝试了以下方法:

Contest.objects.get( pk = id ).image_set.order_by( 'score' )

正如我担心的那样,它不起作用,因为'score'不是可用于查询的数据库字段。

3个回答

47

哦,我当然忘记了Django中新的聚合支持和它的annotate功能。

因此查询可能会像这样:

Contest.objects.get(pk=id).image_set.annotate(score=Sum('vote__value')).order_by( 'score' )

我的结果顺序错了...它们大多数是有序的,但有些被放错了...这可能是什么原因? - Jiaaro
这只是特定情况下的解决方案,而不是针对问题标题“QuerySet order by method”的解决方案:这不是通过方法对查询集进行排序的解决方案。@S.Lott的回答是通用情况下的正确解决方案。 - Stefan Manastirliu

9

你可以非常简单地在Python中编写自己的排序算法。

def getScore( anObject ):
    return anObject.score()
objects= list(Contest.objects.get( pk = id ).image_set)
objects.sort( key=getScore )

这很好运作,因为我们已经对列表进行了排序,并将其提供给模板。

是的,你可以。只需将总和作为查询的一部分即可。 - Aaron Maenpaa
“RelatedManager” 对象没有属性 “sort”。:( - okoman
2
@okoman... 这意味着你没有使用list()函数将其转换为列表。 - Jiaaro
我不知道为什么这个被踩了。在这种情况下,你可以通过在查询中聚合某一列来排序,但通常如果你有任何更复杂的东西,你就束手无策了。尽管这个解决方案很好用,但它相当昂贵... - agscala

2

数据库级别的 order_by 无法通过模型的 Python 方法对 queryset 进行排序。

解决方案是向 Image 模型引入 score 字段,并在每次 Vote 更新时重新计算该字段。这是一种反规范化的方式。当你想要按照它排序时,你就能够做到。


我不确定那是最好的解决方案,但它是一个解决方案。 - Aaron Maenpaa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接