• drf序列化和反序列化


    drf序列化和反序列化

    一、自定义序列化

    # models.py
    from django.db import models
    class User(models.Model):
        CHOICE_SEX = ((0, '男'), (1, '女'))
        name = models.CharField(max_length=100)
        height = models.DecimalField(max_digits=6, decimal_places=2, default=1.85)
        age = models.IntegerField(default=0)
        icon = models.ImageField(upload_to='icon', default='default.jpg')
        sex = models.IntegerField(choices=CHOICE_SEX)
        class Meta:
            db_table = 'db_user'
            verbose_name = '用户表'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.name
    # urls.py
    urlpatterns = [
        url(r'^books/$', views.MyAPIView.as_view()),
        url(r'^books/(?P<pk>d+)/', views.MyAPIView.as_view()),
         # 在路由配置向外面开放的接口 media
        url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})
    ]
    
    
    from api import models
    from django.conf import settings
    from rest_framework.response import Response
    from rest_framework.views import APIView
    class MyAPIView(APIView):
        def get(self, *args, **kwargs):
            pk = kwargs.get("pk", None)
            if pk:
                print(pk)
                user_obj = models.User.objects.filter(pk=pk).first()
                return Response({
                    "status": 0,
                    'msg': "get ok",
                    'result': {
                        "name": user_obj.name,
                        'age': user_obj.age,
                        "icon": f'http://127.0.0.1:8000{settings.MEDIA_URL}{user_obj.icon}',
                        "sex": user_obj.get_sex_display()
                    }
                })
            else:
                user_list = []
                user_list_obj = models.User.objects.all()
    
                for user_obj in user_list_obj:
                    user_list.append(
                        {
                            'result': {
                                "name": user_obj.name,
                                'age': user_obj.age,
                                "icon": f'http://127.0.0.1:8000{settings.MEDIA_URL}{user_obj.icon}',
                                "sex": user_obj.get_sex_display()
                            }
                        }
                    )
                print(user_list)
                return Response({
    
                    "status": 0,
                    'msg': "get ok",
                    'result': user_list
                })
    
        def post(self, *args, **kwargs):
            return Response({
                "status": 1
            })
        
    

    总结:

    1. 通过ORM操作获取数据库拿到资源数据
    2. 将获取的的数据进行序列化,序列化之后将数据返回给前台的数据
    3. 通过Response返回格式化后的数据

    1.1 设置国际化

    # 在settings中设置了国际化才可以显示中文
    LANGUAGE_CODE = 'zh-hans'
    
    TIME_ZONE = 'Asia/Shanghai'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = False
    

    二、通过视图类的序列化和反序列化

    # 在settings中设置了国际化才可以显示中文
    LANGUAGE_CODE = 'zh-hans'
    
    TIME_ZONE = 'Asia/Shanghai'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = False
    

    2.1通过视图类序列化

    自定义序列化类

    # 创建自定义.py并创建序列化类 
    class MySerialize(serializers.Serializer):
        name = serializers.CharField()
        age = serializers.IntegerField()
        # icon = serializers.ImageField()
        # sex = serializers.IntegerField()
        # 小数字段,必须要设置参数
        height = serializers.DecimalField(max_digits=6, decimal_places=3)
    
        # 自定义字段
        gender = serializers.SerializerMethodField()
        icon = serializers.SerializerMethodField()
    	
        # 自定义序列化字段,序列化的属性值由方法来提供,
        #   方法的名字:固定为 get_属性名,
        #   方法的参数:序列化对象,序列化的model对象
        #   强烈建议自定义序列化字段名不要与model已有的属性名重名
        
        def get_gender(self, obj):
            # print(obj, type(obj))  # obj类<class 'api.models.User'>
            # print(self, type(self))  # class 'api.myserialize.MySerialize'>
            return obj.get_sex_display()
    
        def get_icon(self, obj):
            return f'http://127.0.0.1:8000/media/{obj.icon}'
    

    视图类

    
    # 通过serialize实现序列化
    from .myserialize import MySerialize
    class MyAPIView(APIView):
        def get(self, request, *args, **kwargs):
            pk = kwargs.get("pk", None)
            if pk:
                user_obj = models.User.objects.filter(pk=pk).first()
                # 自定义单序列化
                my_serialize = MySerialize(user_obj)
                print(my_serialize.data)
                # if my_serialize.is_valid():
                # res = my_serialize
                return Response({
                    "status": 0,
                    'msg': "get ok",
                    'result': my_serialize.data
    
                })
    
            else:
                user_list_obj = models.User.objects.all()
                # 群序列化
                my_serialize = MySerialize(user_list_obj, many=True)
    
                print(my_serialize.data, type(my_serialize.data))
                return Response({
    
                    "status": 0,
                    'msg': "get ok",
                    'result': my_serialize.data
                })
    

    总结:

    1. 直接将要序列化的数据传给序列化类
    2. 要序列化的数据如果是单个对象,序列化的参数many为False,数据如果是多个对象(list,queryset)序列化的参数many为True
    3. 序列化类的中字段必须与model中字段属性同名,如果不参与序列化的model属性,在序列化类中不做声明,也不会返回
    4. 可以自定义序列化字段,自定义序列化字段用 SerializerMethodField() 作为字段类型,序列化的属性有方法类提供,方法的名字:get_属性名,方法参数:序列化对象,序列化的model对象,注意自定义的序列化字段不要与model已有字段属性重名,返回方法的返回值

    2.2通过视图类反序列化

    class UserCreateSerialize(serializers.Serializer):
        # 系统校验规则
        # 系统必须反序列化的字段,序列化的字段,序列化属性名不是必须有与model属性名字对应
        # 但是与之对应会方便序列化将校验通过的数据与数据库进行交互,
        name = serializers.CharField(min_length=3, max_length=6, required=False,error_messages={
            'required': "姓名必填",
            "min_length": "太短"
        })
        # # 系统可选的反序列化字段:没有提供不进行校验(数据库中有默认值或可以为空),提供了就进行校验
        age = serializers.IntegerField(min_value=3, max_value=6, required=False)
        # icon = serializers.ImageField()
        sex = serializers.IntegerField()
        # 小数字段,必须要设置参数
        height = serializers.DecimalField(max_digits=6, decimal_places=3)
    
        pwd = serializers.CharField(min_length=3, max_length=64)
    
        # 自定义反序列化字段:一定参与校验,且要在校验过程中,将其从入库的数据中取出,剩余与model对应的数据才会入库
        re_pwd = serializers.CharField(min_length=3, max_length=64)
    
        par = serializers.CharField(max_length=10)
    
        # 自定义校验规则:局部钩子,全局钩子
        # 局部钩子: validate_字段名(slef, 字段值)
        # 规则: 成功返回value,校验错误抛异常
        def validate_par(self, value):
            if 'rand' in value.lower():
                raise serializers.ValidationError("名字不能有randy")
            return value
    
        # 全局钩子:validate(self, 所有校验的数据字典)
        # 规则:成功返回attrs,失败抛异常
        def validate(self, attrs):
            pwd = attrs.get("pwd")
            re_pwd = attrs.pop('re_pwd')
            if pwd != re_pwd:
                raise serializers.ValidationError({"re_pwd": "两次密码不能一样!"})
            return attrs
        
        # create重写,
        def create(self, validated_data):
            return models.User.objects.create(**validated_data)
        
    class MyAPIView(APIView):
        # 单增和群增
        def post(self, request, *args, **kwargs):
            # 从请求对象中拿到前台的数据
            # 校验数据是否合法
            # 反序列化成后台Model对象与数据交互
            request_data = request.data
            user_seria = UserCreateSerialize(data=request_data)
    
            # 调用反序列化的校验规则,系统规则,自定义规则(局部钩子,全局钩子)
            result = user_seria.is_valid()
            if result:
                # 校验通过,可以与数据库进行交互增 create,该(update)
                user_obj = user_seria.save()
    
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': MySerialize(user_obj).data
    
                })
            else:
    
                # 校验失败,返回错误信息,在使用 .errors必须先要调用 is_valid()方法
                return Response({
                    "status": 1,
                    "msg": user_seria.errors,
    
                }, status=status.HTTP_400_BAD_REQUEST
                )
    
    

    总结:

    1. 在视图类中,从请求对象中获取前台的数据,校验前台数据是否合法,然后将合法的数据返回给Model对象与数据进行交互,保存数据
    2. 反序列化的数据如果是单个数据,反序列化的参数mangy为False,数据如果是多个字典的列表,反序列化的参数many为True
    3. user_seria.errors,在报错的时候一定要先使用 is_valid()函数
    4. 反序列化类:系统的字段可以在Filed类型中设置系统校验的规则(name=serializers.CharField(min_length=3))
    5. required校验规则对该字段是必校验还是可选校验字段(默认required为True,数据库字段有默认值或可以为空的字段可以将required赋值为False)
    6. 自定义的反序列字段,设置系统校验规则同系统字段,但是需要在自定义校验规则中(局部、全局钩子)将自定义反序列化字段取出(返回剩余的数据与数据库交互)
    7. 局部钩子的方法命名 validate_属性名(self, 属性的value),校验规则为 成功返回属性的value 失败抛出校验错误的异常
    8. 全局钩子的方法命名 validate(self, 所有属性attrs),校验规则为 成功返回attrs 失败抛出校验错误的异常
    9. 校验有个顺序先校验全部声明的字段,只有全部校验成功之后才会去校验局部钩子,然后在校验全局钩子;,
    10. 反序列化需要重写create,update方法

    三、ModelSerializer类实现序列化和反序列化

    3.1 序列化

    from rest_framework.serializers import ModelSerializer
    # 自定反序列化类
    class UserModelSerializer(ModelSerializer):
        # source指定数据的来源,"product.product_id"是models中表的字段
        product_id = serializers.IntegerField(source="product.product_id")
        image_url = serializers.ImageField(source="image.image_url")
        class Meta:
            model = models.User
            fields = ["name", "age", "height", "sex", 'gender']  # 序列化字段
            
    # 自定义插拔序列化字段(在model类中实现):
    #替换了在Serializer类中自定义的序列化字段(SerializerMethodField)
    # 自定义插拔序列化字段一定不参与反序列化过程
    @property
    def gender(self):
        return self.get_sex_display()
    
    from .myserialize import UserModelSerializer
    
    # 视图类
    class MyAPIView(APIView):
        # 单查和群查
        def get(self, request, *args, **kwargs):
            pk = kwargs.get("pk", None)
            if pk:
                user_obj = models.User.objects.filter(pk=pk).first()
    
                if not user_obj:
                    return Response(data={"status": 1, 'msg': "数据不存在"})
    
                # 序列化数据context 将request传递过去则会自动凭借url的资源,比如图片
                user_data = UserModelSerializer(user_obj, many=False,context={"request": request}).data
    
                return Response({
                    'status': 0,
                    'msg': '单查 ok',
                    'results': user_data
                })
    
            # 群查
            user_query = models.User.objects.all()
            # 获取序列化
            user_list_data = UserModelSerializer(user_query, many=True).data
    
            return Response({
                "status": 0,
                "msg": "群查 ok",
                "results": user_list_data
            })
    

    3.2 反序列化

    from rest_framework.serializers import ModelSerializer
    # 自定反序列化类
    from rest_framework.serializers import ModelSerializer
    
    
    class UserModelSerializer(ModelSerializer):
        # 将序列化类与Model类进行绑定
        # 设置序列化与反序列化所有字段(并划分序列化字段与反序列化字段)
        # 设置反序列化的局部与全局钩子
    
        # 自定义反序列化字段,校验规则只能生命自定义反序列化字段是设置,且一定要写write_only
        re_pwd = serializers.CharField(min_length=3, max_length= 5, write_only=True)
    
        class Meta:
            model = models.User
            fields = ["name", "age", "height", "sex", 'gender', "pwd", "re_pwd"]
            extra_kwargs = {
                "name": {
                    'required': True,
                    "min_length": 3,
                    "error_messages": {
                        "min_length": "太短了"
                    }
                },
    
                "age": {
                    # 数据库中有默认值或可以为空字段, required默认为false,前台发送的数据不会进行校验,只有为true时候才会进行校验
                    "required": True,
                    'min_value': 5,
                    "read_only": True,  # 只参与序列化,不参与反序列化进行校验
                },
                "pwd": {
                    "required": True,
                    "write_only": True,  # 只参与反序列化 设置这个就是在反序列化的时候不会返回给前台,不会序列化
    
                },
                "height": {
                    # 只参与序列化,不参与反序列化进行校验,就是校验数据传来了height这个参数,不会对数据进行校验只会返回原先的数据或者是默认的数据
                    "read_only": True,
                }
    
            }
    
        # 局部钩子
        def validate_name(self, value):
            if 'randy' in value.lower():
                raise serializers.ValidationError('名字中不能有randy')
            return value
    	# 全局钩子
        def validate(self, attrs):
            pwd = attrs.get('pwd')
            # 一定要移除
            re_pwd = attrs.pop('re_pwd')
            if pwd != re_pwd:
                raise serializers.ValidationError({'re_pwd': '两次密码不一致'})
            return attrs
    
    
    from .myserialize import UserModelSerializer
    # 视图类
    class MyAPIView(APIView):   
        # 单增和群增
        def post(self, request, *args, **kwargs):
           # 获取校验数据
            request_data = request.data
            user_seria = UserModelSerializer(data=request_data)
    
            # 校验失败直接抛异常,反馈异常信息给前台,只要校验通过代码才会往下执行
            # result = user_seria.is_valid(raise_exception=True)
            result = user_seria.is_valid(raise_exception=True)
            if result:
                # 校验通过,可以与数据库进行交互增 create,该(update)
                user_obj = user_seria.save()
    
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': UserModelSerializer(user_obj).data
    
                })
            else:
    
                # 校验失败,返回错误信息,在使用 .errors必须先要调用 is_valid()方法
                return Response({
                    "status": 1,
                    "msg": user_seria.errors,
    
                }, status=status.HTTP_400_BAD_REQUEST
                )
    

    总结:

    1. 将序列化与反序列化功能整合到一个类,这个类继承 ModelSerializer来实现

    2. 继承ModelSerializer类的资源序列化类内部包含三部分, Meta子类, 局部钩子、全局钩子

      • 注:create和update方法ModelSerializer已经重写了,使用不需要重写,默认不能实现群该功能
      • 通过集成ListSerializer类update方法,实现群该功能,将其绑定给list_serializer_class
    3. 在Meta子类中:

      • 用model来绑定所关联的Model类
      • 用fields来设置所有的序列化反序列化字段
      • 用extra_kwargs来设置系统的校验规则
    4. 重要字段校验规则

      • read_only校验规则:代表该字段只参与序列化,不参与反序列化进行校验,就是校验数据传来了height这个参数,不会对数据进行校验只会返回原先的数据或者是默认的数据
      • write_only校验规则:只参与反序列化 设置这个就是在反序列化的时候不会返回给前台,不会序列化
      • required校验规则:代表该字段在反序列化,在数据库中有默认值或可以为空字段, required默认为false,前台发送的数据不会进行校验,只有为true时候才会进行校验
        • 如果一个字段有默认值或是可以为空,没设置required规则,默认为False,反之默认值为True
        • 如果一个Model字段即没有设置read_only也没设置write_only,该字段默认参与序列化及反序列化
    5. 自定义序列化字段:在Model类中,定义方法属性(可以返回特殊值,还可以完成连表操作),在序列化类的fields属性中可以选择性插拔(重要)自定义插拔序列化字段一定不参与反序列化过程

    6. 自定义反序列化字段:在Serializer类中,自定义校验字段,校验规则也只能在声明字段时设置,自定义的反序列化字段(如re_pwd),必须设置write_only为True 自定义插拔序列化字段一定不参与反序列化过程

    7. user_seria = UserModelSerializer(data=request_data),校验数据要将数据给data,才可以校验

    8. 在全局钩子中 一定要移除重复确认的字段

    9. 在反序列化的时候如果需要字段一定要传则在model中不给他设置默认值,一旦设置了默认值,反序列化的时候就可以不传值

    10. 使用fields来明确字段,__all__表名包含所有字段,也可以写明具体哪些字段,

    11. 使用exclude可以明确排除掉哪些字段

    12. 可以通过read_only_fields指明只读字段,即仅用于序列化输出的字段

    13. 可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数

    在当下的阶段,必将由程序员来主导,甚至比以往更甚。
  • 相关阅读:
    beta冲刺总结-咸鱼
    咸鱼翻身beta冲刺博客集
    事后诸葛亮
    个人作业——软件产品案例分析
    Alpha冲刺博客集
    结对作业第二次
    项目需求分析(团队)
    第二次作业——个人项目实战
    软件工程实践第一次作业--准备
    beta冲刺总结
  • 原文地址:https://www.cnblogs.com/randysun/p/12169986.html
Copyright © 2020-2023  润新知