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