在django中按挖掘评级排序

时间:2011-06-06 17:15:06

标签: python django rating-system

我有两个型号:

class Post(models.Model):
    #some fields
    pass

class Vote(models.Model):
    post = mdoels.ForeignKey(Post)
    value = models.IntegerField()

投票'值可以是1或-1(用户可以向上或向下投票)。

我如何获得按其评级排序的帖子列表?

2 个答案:

答案 0 :(得分:2)

您必须使用注释。 https://docs.djangoproject.com/en/dev/topics/db/aggregation/

class Post(models.Model):         
    name = models.CharField(max_length=20)

class Vote(models.Model):
    post = models.ForeignKey(Post)
    value = models.IntegerField()
    type = models.CharField(max_length=2, choices=(("AW", "Awesomeness"), ("US", "Usefulness")))

然后您需要导入Sum,并且可以获得由vote_total订购的帖子列表,如:

from django.db.models import Sum
Post.objects.annotate(vote_total=Sum('vote__value')).order_by('-vote_total')

编辑:
这是一个例子。创建多个帖子对象和投票对象

Post.objects.get_or_create(id=1, name="post1")
Post.objects.get_or_create(id=2, name="post2")
Post.objects.get_or_create(id=3, name="post3")
Post.objects.get_or_create(id=4, name="post4")
Vote.objects.get_or_create(id=1, post_id=2, value=1, type="AW")
Vote.objects.get_or_create(id=2, post_id=2, value=1, type="AW")
Vote.objects.get_or_create(id=3, post_id=2, value=1, type="US")
Vote.objects.get_or_create(id=4, post_id=2, value=1, type="US")
Vote.objects.get_or_create(id=5, post_id=3, value=-1, type="AW")
Vote.objects.get_or_create(id=6, post_id=3, value=-1, type="AW")
Vote.objects.get_or_create(id=7, post_id=4, value=-1, type="AW")

然后posts = Post.objects.annotate(vote_total=Sum('vote__value')).order_by('-vote_total')将导致[(post.name, post.vote_total) for post in posts]成为。

[(u'post2', 4), (u'post4', -1), (u'post3', -2), (u'post1', None)]

这有一个问题,因为没有帖子的事情最后会发生。由于django的Sum聚合函数将没有条目的总和视为无,而不是0.如果你最初给每个帖子一个值为0的值(或者像reddit的系统那样的值1),这可以解决,你不会得到无:例如:

for p in Post.objects.all():
    Vote.objects.get_or_create(post_id=p.id, value=0)

然后你会得到

>>> [(p.name, p.vote_total) for p in
     Post.objects.annotate(vote_total=Sum('vote__value')).order_by('-vote_total')]
[(u'post2', 4), (u'post1', 0), (u'post4', -1), (u'post3', -2)]

编辑:根据不同投票类型的评论,为投票模型添加类型(上面完成)。 你应该可以做一些事情(用你的应用程序的名称替换'yourappname'来获得正确的数据库表):

select_dict = dict(vote_total = "SELECT SUM(value) FROM yourappname_vote WHERE yourappname_vote.post_id = yourappname_post.id",
                   awesome_total = "SELECT SUM(value) FROM yourappname_vote WHERE yourappname_vote.post_id = yourappname_post.id AND yourappname_vote.type = 'AW' ",
                   useful_total = "SELECT SUM(value) FROM yourappname_vote WHERE yourappname_vote.post_id = yourappname_post.id AND yourappname_vote.type = 'US' ",)

posts = Post.objects.all().extra(select = select_dict).order_by('-vote_total')

>>> [(p.name, p.vote_total, p.awesome_total, p.useful_total) for p in posts]
[(u'post2', 4, 2, 2),
 (u'post1', 0, 0, 0),
 (u'post4', -1, -1, 0),
 (u'post3', -2, -2, 0)]

现在,每个帖子在数据库的一次查询中都会有vote_total,以及awesome_totaluseful_total。有点比ORM更丑,但仍然可读(这就是Sum聚合的工作原理。)。你仍然必须给每一类投票进行初步投票,以便超越出现无序的无。

答案 1 :(得分:0)

我认为这应该有效:

Vote.objects.values_list('post', flat=True).order_by('value')

要颠倒顺序:

Vote.objects.values_list('post', flat=True).order_by('-value')

<强>更新

由于帖子是ForeignKeyPost,我们假设您的模型是这样的:

class Post(models.Model):
    post_id = models.AutoField(primary_key=True)
    post_name = models.CharField(max_length=30, unique=True)

所以这应该有用,它会返回名称:

 Vote.objects.values_list('post__post_name', flat=True).order_by('value')

注释 _

如果您想使用列表进行查询并出现一些错误,只需将其转换为列表:

list(Vote.objects.values_list('post__post_name', flat=True).order_by('value')))