0x00:前言

需求:在评论区实现无限层级评论

使用框架:Django、Django REST Framework

可用于:多级分类标签输出、多级评论输出

在DRF项目开发过程中,在开发评论区模块时,需要对用户评论详情进行输出,起初想在序列化器汇中使用PrimaryKeyRelatedField序列化数据;

"""
文件:serializers.py
"""
# 评论内容序列化器
class CommentModelSerializer(serializers.ModelSerializer):
    # subs = RecursiveCommentSerializer(many=True, read_only=True)
    subs = serializers.PrimaryKeyRelatedField(many=True,read_only=True)
    user = UserDetailSerializer(read_only=True)

    class Meta:
        model = Comment
        fields = '__all__'

但是序列化出来的数据只会显示下级评论的主键id(如下图所示)

PrimaryKeyRelatedField

显然,这种方式不是我们想要的!

于是我又想到了在写一个序列化器采用嵌套序列化器的方法序列化下一级评论的数据

"""
文件:serializers.py
"""
# 二级评论内容序列化器
class CommentModel2Serializer(serializers.ModelSerializer):
    user = UserDetailSerializer(read_only=True)

    class Meta:
        model = Comment
        fields = '__all__'

# 一级评论内容序列化器
class CommentModelSerializer(serializers.ModelSerializer):
    subs = CommentModel2Serializer(many=True,read_only=True)
    user = UserDetailSerializer(read_only=True)

    class Meta:
        model = Comment
        fields = '__all__'

这样子写后,用户的二级评论就可以被序列化出来了!

嵌套序列化器

但是,这样写的话,若是用户有无穷多级评论,那我岂不是要写无穷多个序列化器来进行序列化数据?!

很明显,这个方法也不是我想要的。


0x01:输出多级评论的正确姿势

一、操作视图函数遍历评论

  • 序列化器及模型类文件
"""
文件:models.py
"""
class Comment(models.Model):
    content = models.CharField(max_length=1000, default='', verbose_name='评论内容')
    createtime = models.DateTimeField(auto_now_add=True, null=True, verbose_name="创建日期")
    updatetime = models.DateTimeField(auto_now=True, null=True, verbose_name="修改日期")
    parent = models.ForeignKey('self', related_name='subs', null=True, blank=True,on_delete=models.SET_NULL)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="comments", default=None, verbose_name="用户ID")

    class Meta:
        db_table = "tb_comment"
        verbose_name = "评论"
        verbose_name_plural = verbose_name
        ordering = ['-createtime']  # 指明默认排序

    def __str__(self):
        return self.content

"""
文件:serializers.py
"""
class CommentModelSerializer(serializers.ModelSerializer):
    subs = serializers.PrimaryKeyRelatedField(many=True,read_only=True)
    user = UserDetailSerializer(read_only=True)

    class Meta:
        model = Comment
        fields = '__all__'
  • 视图函数
"""
文件:views.py
"""
from rest_framework import mixins, viewsets
from rest_framework.response import Response
from .serializers import CommentModelSerializer
from .models import Comment

class CommentView(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    serializer_class = CommentModelSerializer
    queryset = Comment.objects.all()

    def serialize_tree(self, queryset):
        for obj in queryset:
            data = self.get_serializer(obj).data
            data['subs'] = self.serialize_tree(obj.subs.all())
            yield data

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        data = self.serialize_tree([instance])
        return Response(data)

这样写虽然将所需数据成功的以想要的形式输出出来了;

  • 输出效果

实现效果

但是使用DRF框架,肯定是想要能使代码更加简便,于是我们有了姿势二。


二、使用序列化器递归序列化数据

  • 视图文件及模型类文件
"""
文件:view.py
"""
from rest_framework import viewsets, mixins, views
from .serializers import CommentModelSerializer
from .models import Comment

# Create your views here.

class CommentView(mixins.RetrieveModelMixin,viewsets.GenericViewSet):
    serializer_class = CommentModelSerializer
    queryset = Comment.objects.all()


"""
文件:models.py
"""
class Comment(models.Model):
    content = models.CharField(max_length=1000, default='', verbose_name='评论内容')
    createtime = models.DateTimeField(auto_now_add=True, null=True, verbose_name="创建日期")
    updatetime = models.DateTimeField(auto_now=True, null=True, verbose_name="修改日期")
    parent = models.ForeignKey('self', related_name='subs', null=True, blank=True,on_delete=models.SET_NULL)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="comments", default=None, verbose_name="用户ID")

    class Meta:
        db_table = "tb_comment"
        verbose_name = "评论"
        verbose_name_plural = verbose_name
        ordering = ['-createtime']  # 指明默认排序

    def __str__(self):
        return self.content
  • 序列化器
"""
文件:serializers.py
"""
# 递归实现无限层级评论回复
class RecursiveCommentSerializer(serializers.Serializer):
    def to_representation(self, value):
        serializer = self.parent.parent.__class__(value, context=self.context)
        return serializer.data

# 评论内容序列化器
class CommentModelSerializer(serializers.ModelSerializer):
    subs = RecursiveCommentSerializer(many=True, read_only=True)
    user = UserModelSerializer(read_only=True)

    class Meta:
        model = Comment
        fields = '__all__'
  • 输出效果

实现效果


0x02: 参考文献

Last modification:October 23rd, 2020 at 07:42 pm
给狐宝打点钱⑧