zoukankan      html  css  js  c++  java
  • DRF的序列化和反序列化

    一、DRF的序列化和反序列化介绍

    1. 概念和作用

    • DRF的序列化:就是把后端的models对象通过DRF序列化处理,变成前端能识别的数据
    • DRF的反序列化:就是前端的提交的数据通过DRF的反序列化,进行对数据的校验,之后变成models对象保存到数据库

    2.序列化家族

    • 下面的每一个类都可以实现序列化和反序列化

    (1)Serializer类

    • Serializer类是底层序列化类,高级的序列化类的底层功能是由它实现的

    • 针对单表、多表的单查群查和单增单改操作

    • 在使用Serializer类时,反序列化中的插入和更新数据库操作都要自己重写父类的create和update方法

    (2)ModelSerializer类

    • ModelSerializer类是高级的序列化类,完成针对单表、多表的单查群查和单增单改操作序列化和反序列化
    • 在使用ModelSerializer类时,反序列化中的插入和更新数据库操作不需要重写create和update方法

    (3)ListSerializer类

    • ListSerializer类是群操作序列化类,辅助完成单表、多表的群增和群改操作

    3. DRF的序列化和反序列化的使用方法

    • 在对应的app下,新建一个专门用来处理序列化和反序列化的py文件,通过导入DRF的序列化家族,让自定义的类继承序列化家族中的一个类即可。其实DRF的序列化和反序列化和django的forms组件的用法差不多。

    • 因为序列化家族中的Serializer类封装程度没有ModelSerializer类完全,所以ModelSerializer类使用起来更佳的友好、简便。因此下面我们只详细介绍ModelSerializer类的使用方法

    • views文件的视图类的写法

    # drf序列化过程
    # 视图类序列化过程
    # 1)ORM操作得到数据
    # 2)将数据序列化成可以返回给前台的数据
    # 3)返回数据给前台
    
    # 视图类反序列化过程
    # 1)从请求对象中获取前台提交的数据
    # 2)交给序列化类完成反序列化(数据的校验)
    # 3)借助序列化类完成数据入库
    # 4)反馈给前台处理结果
    

    二、Serializer类:序列化与反序列化

    • 针对单表、多表的单查群查和单增单改操作

    • 下面的序列化和但序列化可以写在一个类中,此时就要在字段类中加入write_only 或者 read_only

    1. views文件中

    # views文件中:
    
    """
    # 视图类序列化过程,就是三步
    # 1)ORM操作得到数据
    # 2)将数据序列化成可以返回给前台的数据
    # 3)返回数据给前台
    """
    from . import serializers
    class UserV2APIView(APIView):
        
        # 序列化*************************************************
        # 单查群查
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if pk:
                user_obj = models.User.objects.filter(is_delete=False, pk=pk).first()
                if not user_obj:
                    return Response({
                        'status': 1,
                        'msg': 'pk error',
                    }, status=400)
    
                user_ser = serializers.UserSerializer(user_obj, many=False)  # many默认为False
                user_obj_data = user_ser.data
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': user_obj_data
                })
            else:
                # 将对象对外提供的字段,经整个序列化过程封装,形成序列化类
                user_query = models.User.objects.filter(is_delete=False).all()
    
                user_ser = serializers.UserSerializer(user_query, many=True)  # many默认是false,many的意思就是括号内传的对象是单个还是多个,多个就要把many置为True
                user_list_data = user_ser.data
    
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': user_list_data
                })
            
            
        # 反序列化**********************************************
        # 单增
        def post(self, request, *args, **kwargs):
            request_data = request.data
            user_ser = serializers.UserDeSerializer(data=request_data)
            if user_ser.is_valid():
                # 入库
                user_obj = user_ser.save()
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': serializers.UserSerializer(user_obj).data  # 将入库得到的user对象重新序列化的数据返回给前台
                })
            else:
                return Response({
                    'status': 1,
                    'msg': user_ser.errors,
                })
    
    

    2. 新建的用来序列化和反序列化处理的文件中

    # serializers文件中
    
    # 导入DRF的序列化家中的serializers类
    from rest_framework import serializers
    from django.conf import settings
    from . import models
    
    
    # 序列化操作*********************************************************
    
    """
    1)设置序列化字段,字段名与字段类型要与处理的model类属性名对应一只(参与序列化的字段不需要设置条件,因为只是后端提供数据给前端)
    2)想要给前端哪些数据,就序列化哪些数据即可,没写的字段不会参与序列化,也就不会提供给前端
    3)自定义序列化字段(方法一),字段类为固定的SerializerMethodField(),字段的值由 get_自定义字段名(self, model_obj) 方法提供,所以你要手动写这个方法,并返回一个值作为自定义的字段的值(一般值都与参与序列化的model对象(model_obj)有关),自定义的序列化字段默认read_only=True,且不可修改
    """
    class UserSerializer(serializers.Serializer):
        # 1)字段名与字段类型要与处理的model类对应
        # 2)不提供的字段,就不参与序列化给前台
        # 3)可以自定义序列化字段,采用方法序列化,方法固定两个参数,第二个参数就是参与序列化的model对象
        #       (注意:不建议自定义字段名与模型类中的字段名重名,由get_自定义字段名方法的返回值提供字段值)
        username = serializers.CharField()
        # sex = serializers.IntegerField()  # sex是模型类中定义为有chioce属性的字段
    
        # sex = serializers.SerializerMethodField()  # 不建议这样命名
        gender = serializers.SerializerMethodField()
    
        def get_gender(self, obj):
            return obj.get_sex_display()
    
        # 注:在高级序列化与高级视图类中,drf默认帮我们处理图片等子资源
        icon = serializers.SerializerMethodField()
    
        def get_icon(self, obj):
            return '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, obj.img)
        
        
        
    # 反序列化操作**********************************************************
    
    """
    1)系统校验字段与自定义校验字段定义没有区别:字段 = serializers.字段类型(条件)
    2)自定义校验字段是不能直接入库的,需要设置入库规则,或将其移除不入库(这类字段就是参与全局校验用的)
    3)所有字段都可以设置对应局部钩子进行校验,钩子方法 validate_字段名(self, 字段值value)
        规则:成功直接返回value,失败抛出校验失败信息ValidationError('错误信息')
    4)一个序列化类存在一个全局钩子可以对所有字段进行全局校验,钩子方法 validate(self, 所有字段值字典attrs)
        规则:成功直接返回attrs,失败抛出校验失败信息ValidationError({'异常字段', '错误信息'})
    5)重写create方法实现增入库,返回入库成功的对象
    6)重写update方法实现改入库,返回入库成功的对象
    """
    class UserDeSerializer(serializers.Serializer):
        # 系统校验字段
        username = serializers.CharField(min_length=3, max_length=16, error_messages={
            'min_length': '太短',
            'max_length': '太长'
        })
        password = serializers.CharField(min_length=3, max_length=16)
    
        # 不写,不参与反序列化,写就必须参与反序列化(但可以设置required=False取消必须)
        # required=False的字段,前台不提供,走默认值,提供就一定进行校验;不写前台提不提供都采用默认值
        sex = serializers.BooleanField(required=False)
    
        # 自定义校验字段:从设置语法与系统字段没有区别,但是这些字段不能参与入库操作,需要在全局钩子中,将其取出
        re_password = serializers.CharField(min_length=3, max_length=16)
    
        # 局部钩子:
        #   方法就是 validate_校验的字段名(self, 校验的字段数据)
        #   校验规则:成功直接返回value,失败抛出校验失败信息
        def validate_username(self, value):
            if 'g' in value.lower():
                raise serializers.ValidationError('名字中不能有g')
            return value
    
        # 全局钩子:
        #   方法就是 validate(self, 所有的校验数据)
        #   校验规则:成功直接返回attrs,失败抛出校验失败信息
        def validate(self, attrs):
            password = attrs.get('password')
            re_password = attrs.pop('re_password')
            if password != re_password:
                raise serializers.ValidationError({'re_password': '两次密码不一致'})
            return attrs
    
        # 在视图类中调用序列化类的save方法完成入库,Serializer类能做的增入库走create方法,改入库走update方法
        # 但Serializer没有提供两个方法的实现体
        def create(self, validated_data):
            return models.User.objects.create(**validated_data)
    
        # instance要被修改的对象,validated_data代表校验后用来改instance的数据
        def update(self, instance: models.User, validated_data):
            # 用户名不能被修改
            validated_data.pop('username')
            models.User.objects.filter(pk=instance.id).update(**validated_data)
            return instance
    
    

    三、ModelSerializer类:序列化与反序列化

    • 针对单表、多表的单查群查和单增单改操作

    1. views文件中

    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from django.conf import settings
    from . import models
    
    class UserV3APIView(APIView):
        
        # 序列化***********************************************************************
        # 单查群查
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if pk:
                user_obj = models.User.objects.filter(is_delete=False, pk=pk).first()
                if not user_obj:
                    return Response({
                        'status': 1,
                        'msg': 'pk error',
                    }, status=400)
    
                user_ser = serializers.UserModelSerializer(user_obj, many=False)
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': user_ser.data
                })
            else:
                user_query = models.User.objects.filter(is_delete=False).all()
    
                user_ser = serializers.UserModelSerializer(user_query, many=True)
    
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': user_ser.data
                })
    	
        # 反序列化*******************************************************************
        # 单增
        def post(self, request, *args, **kwargs):
            user_ser = serializers.UserModelSerializer(data=request.data)
            if user_ser.is_valid():
                # 入库
                user_obj = user_ser.save()
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': serializers.UserModelSerializer(user_obj).data
                })
            else:
                return Response({
                    'status': 1,
                    'msg': user_ser.errors,
                })
    

    2. 新建的用来序列化和反序列化处理的文件中

    • 这里新建的文件名为serializers.py
    • ModelSerializer类的序列化和反序列化都在一起做,所以需要我们根据不同情况对相应的字段进行 序列化还是反序列化还是既需要序列化又需要反序列化 的限制。
    # serializers文件中
    
    """ ModelSerializer类序列化与反序列化总结
    1)序列化类继承ModelSerializer,所以需要在配置类Meta中进行序列化和反序列化的配置
    2)model配置:绑定序列化相关的模型表
    3)fields配置:采用 插拔式 设置所有参与序列化与反序列化字段
    4)extra_kwargs配置(配置系统字段的校验条件、错误信息和该字段的序列化、反序列化选择):
        划分系统字段为三种:只读,即只序列化(read_only)、只写,即只反序列化(write_only)、可读可写,即序列化和反序列化都有(为空就是可读又可写)
        字段是否必须:required=True/False
        选填字段(模型表中有默认值的字段):若在extra_kwargs进行配置,一般设置required=False(当然具体情况具体分析),前端不提供则用默认值,前端提供,则用提供的
    5)自定义序列化字段:(自定义的序列化字段默认read_only=True,且不可修改)
        第一种(不提倡):在序列化类中用SerializerMethodField()来实现,若是以此方法自定义了序列化字段,则必须在field中配置该字段,否则报错
        第二种(提倡):在模型类中用@property来实现,可插拔
    6)自定义反序列化字段:
        同Serializer类,且校验条件只能在其定义时字段类的括号中设置,或是在钩子中设置,而在extra_kwargs中对其设置是无效的
        自定义的反序列化字段必须设置 write_only=True
        自定义的反序列化字段最后要pop出来
    7)局部钩子,全局钩子都同Serializer类用法一致。(在配置类Meta外部配置钩子)
    8)不需要重写create和update方法
    """
    class UserModelSerializer(serializers.ModelSerializer):
        # 第一种自定义序列化字段:该字段必须在fields中设置,不推荐这种自定义序列化字段的方式
        # gender = serializers.SerializerMethodField()
        # def get_gender(self, obj):
        #     return obj.get_sex_display()
    
    
        # 自定义反序列化字段同Serializer类,且规则只能在此声明中设置,或是在钩子中设置,
        # 在extra_kwargs中对其设置的无效
        # 注:自定义反序列化字段必须设置 write_only
        re_password = serializers.CharField(min_length=3, max_length=16, write_only=True)
    
        class Meta:
            model = models.User
            # fields采用 插拔式 设置所有参与序列化与反序列化字段
            fields = ('username', 'gender', 'icon', 'password', 'sex', 're_password')
            extra_kwargs = {
                'username': {  # 系统字段,不设置read_only和write_only,则默认都参加
                    'min_length': 3,
                    'max_length': 10,
                    'error_messages': {  # 当在settings文件中配置了国际化文字为中文后,就不需要再自己写中文的错误信息了,在前端会自动转成中文的错误信息
                        'min_length': '太短',
                        'max_length': '太长'
                    }
                },
                'gender': {
                    'read_only': True,  # 自定义的序列化字段默认就是read_only,且不能修改,但可以省略不写
                },
                'password': {
                    'write_only': True,
                },
                'sex': {  # 像sex有默认值的字段,为选填字段('required': True可以将其变为必填字段)
                    'write_only': True,
                    # 'required': True
                }
            }
    
    
        # 局部全局钩子同Serializer类,是与 Meta类的代码 同缩进的
        def validate_username(self, value):
            if 'g' in value.lower():
                raise serializers.ValidationError('名字中不能有g')
            return value
    
        def validate(self, attrs):
            password = attrs.get('password')
            re_password = attrs.pop('re_password')
            if password != re_password:
                raise serializers.ValidationError({'re_password': '两次密码不一致'})
            return attrs
    
        # create和update方法不需要再重写,ModelSerializer类已提供,且支持所有关系下的连表操作
    

    3. models文件中

    from django.db import models
    class User(models.Model):
        SEX_CHOICES = (
            (0, '女'),
            (1, '男'),
        )
    
        username = models.CharField(max_length=64, verbose_name='用户名', blank=True, unique=True)
        password = models.CharField(max_length=64, verbose_name='密码')
        sex = models.IntegerField(choices=SEX_CHOICES, default=0, verbose_name='性别')
        img = models.ImageField(upload_to='img', default='img/default.png', verbose_name='头像')
        # 开发中,数据不会直接删除,通过字段控制
        is_delete = models.BooleanField(default=False, verbose_name='是否注销')
        # 数据库数据入库,一般都会记录该数据第一次入库时间,有时候还会记录最后一次更新时间
        created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    
        # 第二种自定义序列化字段方式(插拔式,提倡使用)
        @property
        def gender(self):
            return self.get_sex_display()
    
        @property
        def icon(self):
            from django.conf import settings
            return '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, self.img)
    
    
        class Meta:  # 配置类,给所属类提供配置信息
            db_table = 'old_boy_user'
            verbose_name_plural = '用户表'
    
        def __str__(self):  # 不要在这里进行连表操作,比如admin页面可能会崩溃
            return self.username
    
  • 相关阅读:
    【NOIP 2003】 加分二叉树
    【POJ 1655】 Balancing Act
    【HDU 3613】Best Reward
    【POJ 3461】 Oulipo
    【POJ 2752】 Seek the Name, Seek the Fame
    【POJ 1961】 Period
    【POJ 2406】 Power Strings
    BZOJ3028 食物(生成函数)
    BZOJ5372 PKUSC2018神仙的游戏(NTT)
    BZOJ4836 二元运算(分治FFT)
  • 原文地址:https://www.cnblogs.com/Mcoming/p/12098842.html
Copyright © 2011-2022 走看看