Django查询:如何按投票数量排序帖子?

时间:2018-12-16 12:58:31

标签: django django-models django-templates django-views django-queryset

我目前正在使用Django的网站上工作,人们可以在其中写一个故事,这个故事可以由自己或他人来支持。以下是“个人资料”,“故事”和“ Upvote”的类:

class Profile(AbstractBaseUser, PermissionsMixin):
  email = models.EmailField(unique=True)
  first_name = models.CharField(max_length=30, null=True)
  last_name = models.CharField(max_length=30, null=True)


class Story(models.Model):
  author = models.ForeignKey('accounts.Profile', on_delete=models.CASCADE, related_name="author")
  title = models.CharField(max_length=50)
  content = models.TextField(max_length=10000)


class Upvote(models.Model):
  profile = models.ForeignKey('accounts.Profile', on_delete=models.CASCADE, related_name="upvoter")
  story = models.ForeignKey('Story', on_delete=models.CASCADE, related_name="upvoted_story")
  upvote_time = models.DateTimeField(auto_now=True)

如您所见,Upvote使用两个外键来存储upvoter和相关的故事。现在,我要进行查询,以列出所有故事,并按其获得的支持数进行排序。我已经尽力提出一些查询,但这并不是我要搜索的。

这根本不起作用,因为出于某种原因,它只能按照创建的顺序提供所有故事。它还包含重复项,尽管我希望按故事将它们分组。

hot_feed = Upvote.objects.annotate(upvote_count=Count('story')).order_by('-upvote_count')

这是一种作品。但是,如果我尝试访问模板中的一个特定故事,它只会给我ID。因此,我无法从该ID中获取标题,作者和内容,因为它只是一个整数,而不是对象。

hot_feed = Upvote.objects.values('story').annotate(upvote_count=Count('story')).order_by('-upvote_count')

有人可以帮我找到正在搜索的查询吗?

1 个答案:

答案 0 :(得分:2)

您从错误的模型中查询,您基本上是在按故事数或类似的顺序获取Upvote

但是您的概率希望按投票数获取Story,因此您需要使用Story作为“查询集根”,并用投票数进行注释:

Story.objects.annotate(
    upvote_count=Count('upvoted_story')
).order_by('-upvote_count')

我认为您的related_name中的story有点“ 误导”。 related_name是关系“反向”的名称,因此可能更好的名称是upvotes

class Upvote(models.Model):
    profile = models.ForeignKey(
        'accounts.Profile',
        on_delete=models.CASCADE,
        related_name='upvotes'
    )
    story = models.ForeignKey(
        'Story',
        on_delete=models.CASCADE,
        related_name='upvotes'
    )
    upvote_time = models.DateTimeField(auto_now=True)

在这种情况下,查询为:

Story.objects.annotate(
    upvote_count=Count('upvotes')
).order_by('-upvote_count')