You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django Rest Framework中序列化嵌套层级评论

Django Rest Framework 实现评论嵌套层级结构

需求说明

当前API返回平级评论列表,示例如下:

"results": [
        {
            "id": 12,
            "blog_slug": "rest-api-django",
            "comment": "Parent Comment",
            "main_comment_id": null
        },
        {
            "id": 13,
            "blog_slug": "rest-api-django",
            "comment": "Child Comment of 12",
            "main_comment_id": 12
        },
        {
            "id": 14,
            "blog_slug": "rest-api-django",
            "name": "Mr Ahmed",
            "comment": "Child Comment OF 13",
            "comment_uuid": "c0b389bc-3d4a-4bda-b6c6-8d3ef494c93c",
            "main_comment_id": 13
        }
    ]

期望改造为子评论嵌套在对应父评论children字段下的层级结构,示例如下:

{
  "results": [
    {
      "id": 12,
      "blog_slug": "rest-api-django",
      "comment": "Parent Comment",
      "main_comment_id": null,
      "children": [
        {
          "id": 13,
          "blog_slug": "rest-api-django",
          "comment": "Child Comment of 12",
          "main_comment_id": 12,
          "children": [
            {
              "id": 14,
              "blog_slug": "rest-api-django",
              "name": "Mr Ahmed",
              "comment": "Child Comment OF 13",
              "comment_uuid": "c0b389bc-3d4a-4bda-b6c6-8d3ef494c93c",
              "main_comment_id": 13
            }
          ]
        }
      ]
    }
  ]
}

现有代码

模型代码

class BlogComment(models.Model):
      user  =  models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
      blog_slug = models.CharField(max_length=500)
      main_comment_id = models.ForeignKey("self",on_delete=models.CASCADE,blank=True,null=True)
      name = models.CharField(max_length=250)
      email = models.CharField(max_length=250)
      comment =  models.TextField()
      is_published = models.BooleanField(True)
      comment_uuid =  models.CharField(max_length=250)

序列化器代码

class BlogCommentSerializers(serializers.Serializer):
      id =  serializers.IntegerField(read_only=True)
      comment_uuid  =  serializers.CharField(read_only=True)
      blog_slug = serializers.CharField(max_length=500)
      main_comment_id = serializers.CharField(required=False)
      name = serializers.CharField(max_length=250)
      email = serializers.CharField(max_length=250)
      comment = serializers.CharField(style={'base_template': 'textarea.html'},allow_blank=False,required=True)
      is_published = serializers.BooleanField(default=True)

      def to_representation(self, instance):
        data = {
            'id': instance.id,
            'blog_slug': instance.blog_slug,
            'name': instance.name,
            'email': instance.email,
            'comment': instance.comment,
            "comment_uuid": instance.comment_uuid, 
            "main_comment_id": instance.main_comment_id
        }
        
        if instance.main_comment_id:
            data['main_comment_id'] = instance.main_comment_id.id  # Change to the ID number
        
        return data

API视图代码

if request.method == 'GET':
         paginator = PageNumberPagination()
         paginator.page_size = 1
         blogs = Blog.objects.filter(is_approved=True).all()
         result_page = paginator.paginate_queryset(blogs, request)
         serializer = BlogSerialzers(result_page, many=True)
         paginated_response = paginator.get_paginated_response(serializer.data)
        
         if paginated_response.get('next'):
            paginated_response.data['next'] = paginated_response.get('next')
         if paginated_response.get('previous'):
            paginated_response.data['previous'] = paginated_response.get('previous')

         return paginated_response

实现步骤

1. 给模型添加反向关联名称

修改BlogComment模型的main_comment_id字段,添加related_name方便查询子评论:

main_comment_id = models.ForeignKey("self", on_delete=models.CASCADE, blank=True, null=True, related_name='children')

2. 修改序列化器实现嵌套

改用ModelSerializer简化代码,添加递归嵌套的子评论字段:

class BlogCommentSerializers(serializers.ModelSerializer):
    # 递归序列化子评论
    children = serializers.SerializerMethodField()
    main_comment_id = serializers.IntegerField(allow_null=True, required=False)

    class Meta:
        model = BlogComment
        fields = ['id', 'comment_uuid', 'blog_slug', 'main_comment_id', 'name', 'email', 'comment', 'is_published', 'children']

    def get_children(self, obj):
        # 获取当前评论的已发布子评论,递归序列化
        child_comments = obj.children.filter(is_published=True)
        return BlogCommentSerializers(child_comments, many=True, context=self.context).data

    def to_representation(self, instance):
        data = super().to_representation(instance)
        # 处理main_comment_id为null的情况
        if not data['main_comment_id']:
            data['main_comment_id'] = None
        return data

3. 调整视图返回层级评论

如果是单篇博客的评论接口,修改视图只返回顶级评论(main_comment_id为null):

def blog_comments(request, blog_slug):
    if request.method == 'GET':
        # 获取当前博客下的已发布顶级评论
        top_comments = BlogComment.objects.filter(
            blog_slug=blog_slug,
            main_comment_id__isnull=True,
            is_published=True
        ).order_by('-id')
        
        paginator = PageNumberPagination()
        paginator.page_size = 10
        result_page = paginator.paginate_queryset(top_comments, request)
        serializer = BlogCommentSerializers(result_page, many=True)
        
        return paginator.get_paginated_response(serializer.data)

如果要在博客列表接口中包含评论,修改BlogSerialzers添加评论字段:

class BlogSerialzers(serializers.ModelSerializer):
    comments = serializers.SerializerMethodField()

    class Meta:
        model = Blog
        fields = ['id', 'slug', 'title', 'comments']  # 补充其他博客字段

    def get_comments(self, obj):
        top_comments = BlogComment.objects.filter(
            blog_slug=obj.slug,
            main_comment_id__isnull=True,
            is_published=True
        )
        return BlogCommentSerializers(top_comments, many=True).data

注意事项

  • 递归序列化层级过深会影响性能,可在get_children中添加深度限制,或预查询所有评论后在内存中构建层级结构。
  • 始终过滤is_published=True的评论,避免返回未发布内容。

内容的提问来源于stack exchange,提问作者boyenec

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 14:34:51