【问题标题】:Django, creating custom order/filter based on value from another ModelDjango,根据来自另一个模型的值创建自定义订单/过滤器
【发布时间】:2021-10-02 21:36:14
【问题描述】:

我正在创建一个 Reddit 克隆,目前正在尝试实现帖子排序功能。我坚持按“最多投票”对帖子进行排序。我相信我的模型/数据库结构不正确,并希望我不必重做它,因为我的投票系统运行良好。

// models.py
class Post(models.Model):
    title = models.CharField(max_length=300)
    content = models.TextField()
    date_created = models.DateTimeField(verbose_name="date_created", auto_now_add=True)
    date_edited = models.DateTimeField(verbose_name="date_edited", auto_now=True)
    author = models.ForeignKey(User, related_name="posts", on_delete=models.CASCADE)
    subreddit = models.ForeignKey(Subreddit, on_delete=models.CASCADE)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["title", "subreddit"], name="sameTitle_in_sameSubreddit_notAllowed")
        ]

    def __str__(self):
        return self.title

class Vote(models.Model):
    original_post = models.ForeignKey(Post, related_name="post_votes", on_delete=models.CASCADE, null=True)
    original_comment = models.ForeignKey(Comment, related_name="comment_votes", on_delete=models.CASCADE, null=True)
    owner = models.ForeignKey(User, related_name="votes", on_delete=models.CASCADE)
    vote_choice = models.IntegerField(choices=((1, "UP"), (2, "DOWN")))

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["original_post", "owner"], name="sameOwner_samePost_notAllowed"),
            models.UniqueConstraint(fields=["original_comment", "owner"], name="sameOwner_sameComment_notAllowed")
        ]

查看“投票”模型,我能够在视图(测试目的)中编写原始 SQL 查询,该查询将“original_post”列按最高投票数排序并将其返回到列表中。是否可以获取此查询的结果并将自定义排序应用于“发布”查询集。因此,例如,按“post_id”对帖子进行排序,但按照“最多投票列表”的相同顺序对 iD 进行排序。

// views.py
class PostWithMostUpVotes(viewsets.ModelViewSet):
        def list(self, request, *args, **kwargs):
            queryset = Vote.objects.raw("SELECT id, original_post_id FROM reddit_api_vote WHERE original_post_id IS NOT NULL AND vote_choice='1' GROUP BY original_post_id ORDER BY count(vote_choice) DESC;")
            serializer = VoteSerializer(queryset, many=True)
            sorted_list = []
            for x in serializer.data:
                sorted_list.append(x["original_post"])
            return Response(sorted_list)

我用 Postman 测试了上述视图,它确实按预期工作。

// urls.py
router.register('mostupvotes', PostWithMostUpVotes, 'mostupvotes')

original_post 9 个帐户获得最多支持。仅供参考:1=赞成,2=反对 iD 为 9 的帖子获得最多赞,因此它出现在列表的开头。

【问题讨论】:

    标签: python django sorting


    【解决方案1】:

    您应该充分利用 DRF 的所有内置功能。为此,您需要做的就是实现一个get_queryset 方法。

    class PostView(viewsets.ModelViewSet):
        serializer_class = PostSerializer
        queryset = Post.objects.all()
    

    这与 DRF 中大多数视图所需的值接近。

    无论如何,如果您想按投票数对帖子进行排序,只需对查询集进行排序即可。我们还将用计数注释查询

    您可以在类的初始查询集中执行此操作,也可以实现get_queryset 方法以动态生成查询集。

    class PostView(viewsets.ModelViewSet):
        serializer_class = PostSerializer
        queryset = Post.objects.all()
    
        def get_queryset(self):
            qs = super().get_queryset()
            with_counts = qs.annotate(vote_count=Count('post_votes'))
            hotness_order = with_counts.order_by('-vote_count')
            return hotness_order
    

    或者,您也可以在 DRF 中指定ordering fields,以便根据来自客户的请求轻松订购。这假设您为此配置了一个后端。

    class PostView(viewsets.ModelViewSet):
        serializer_class = PostSerializer
        queryset = Post.objects.annotate(vote_count=Count('post_votes')
        ordering_fields = ['vote_count', 'date_created']
    

    【讨论】:

    • 谢谢。这行得通!我不知道您可以按“related_name”订购。
    • @BrandonHarmon 很高兴这对你有用。如果它回答了您的问题,请考虑投票/接受答案:-)
    【解决方案2】:

    在@sytech 的大力帮助下,我能够完全按照我的意愿执行查询。按照最多点赞数到最少点赞数的顺序对帖子进行排序。我还能够在原始视图集中为其创建 URL 路径。

    class PostViewSet(viewsets.ModelViewSet):
        queryset = Post.objects.all()
        serializer_class = PostSerializer
    
        @action(detail=False)
        def most_upvotes(self, request, *args, **kwargs):
            up_count = Count("post_votes", filter=Q(post_votes__vote_choice=1))
            posts = Post.objects.annotate(up_count=up_count).order_by("-up_count")
            serializer = PostSerializer(posts, many=True)
            return Response(serializer.data)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-05
      • 2013-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-21
      • 1970-01-01
      相关资源
      最近更新 更多