Сортировать по рейтингу в стиле джанго - PullRequest
1 голос
/ 06 июня 2011

У меня есть две модели:

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

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

Значение голоса может быть 1 или -1 (пользователь может голосовать за или против).

Как я могу получить списоксообщений, упорядоченных по их рейтингу?

Ответы [ 2 ]

2 голосов
/ 06 июня 2011

Вы должны использовать аннотацию.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)]

Это имеет проблему, так как вещи без постов идут в оченьконец.Так как функция агрегации Sum в django принимает сумму без записей как None, а не 0. Это может быть решено, если вы изначально дали каждому посту голосование со значением 0 (или значением 1, как в системе reddit), вы не получите None.: например:

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_total и useful_total в одном запросе к базе данных.Немного страшнее, чем ORM, но все же вполне читабельно (именно так работает агрегация Sum).Вам все равно придется дать каждой категории голосов первоначальный голос, чтобы обойти «Ни один», появляющийся не по порядку.

0 голосов
/ 06 июня 2011

Я думаю, что это должно работать:

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

Чтобы инвертировать порядок:

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

ОБНОВЛЕНИЕ

Поскольку сообщение является *От 1011 * до Post, давайте предположим, что ваша модель выглядит примерно так:

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')

note _

Если вы хотите сделать запросы со списком, и появляется какая-то ошибка, просто преобразуйте ее в список:

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