• DRF ---- 三流 Serializer(偏底层)


    三流:补充知识点

    import sys
    # 标准输出流
    sys.stdout.write('123') # print() 会换行 但是stdout不换
    sys.stdout.write('123
    ')
    
    # 标准输入流
    res = sys.stdin.readline()
    print(res)
    
    # 标准错误流
    sys.stderr.write('123') # 结果是红色的 异步执行 123 永远比321先打印
    sys.stderr.write('321') 
    

    序列化家族

    """
    1、Serializer类:底层序列化类 - 了解类
    	重点:单表序列化
    
    2、ModelSerializer类:模型序列化类 - 核心类
    	重点:多表序列化
    	
    3、ListSerializer类:群操作序列化类 - 辅助类
    	重点:辅助完成单表多表群增群改操作
    """
    

    知识点:Serializer(偏底层)、ModelSerializer(重点)、ListModelSerializer(辅助群改)

    Serializer(偏底层)

    1.instance 以及 data 参数

    1)用于序列化时,将模型类对象传入instance参数
    
    2)用于反序列化时,将要被反序列化的数据传入data参数
    
    3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如
    # serializer = AccountSerializer(account, context={'request': request})
    

    models.py 准备

    class User(models.Model):
        SEX_CHOICES = [
            [0, '男'],
            [1, '女'],
        ]
        name = models.CharField(max_length=64)
        pwd = models.CharField(max_length=32)
        phone = models.CharField(max_length=11, null=True, default=None)
        sex = models.IntegerField(choices=SEX_CHOICES, default=0)
        icon = models.ImageField(upload_to='icon', default='icon/default.jpg')
    
        class Meta:
            db_table = 'old_boy_user'
            verbose_name = '用户'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return '%s' % self.name
    

    2. 序列化

    1)设置需要返回给前台 那些model类有对应的 字段,不需要返回的就不用设置了

    2)设置方法字段,字段名可以随意,字段值由 get_字段名 方法提供,来完成一些需要处理在返回的数据(如choice字段)

    自定义serializer

    创建自定义py文件, 自定义serializer

    # 序列化组件 - 为每一个model类提供一套序列化工具类
    # 序列化组件的工作方式与django froms组件非常相似
    
    from rest_framework import serializers, exceptions
    from django.conf import settings
    
    from . import models
    
    # 自定义后继承Serializer
    class UserSerializer(serializers.Serializer):
        name = serializers.CharField()
        phone = serializers.CharField()
        # 序列化提供给前台的字段个数由后台决定,可以少提供,
        # 但是提供的数据库对应的字段,名字一定要与数据库字段相同
        # sex = serializers.IntegerField() 得到的结果不是自己想要的
        # icon = serializers.ImageField()
    

    gender icon 字段 序列化方法:

    第一种方法

        # 自定义序列化属性
        # 属性名随意,值由固定的命名规范方法提供:
        # get_属性名(self, 参与序列化的model对象)
        # 返回值就是自定义序列化属性的值
    
        gender = serializers.SerializerMethodField()
        def get_gender(self, obj):
            # choice类型的解释型值 get_字段_display() 来访问
            return obj.get_sex_display()
        
        icon = serializers.SerializerMethodField()
        def get_icon(self, obj):
            # settings.MEDIA_URL: 自己配置的 /media/,给后面高级序列化与视图类准备的
            # obj.icon不能直接作为数据返回,因为内容虽然是字符串,但是类型是ImageFieldFile类型
            return '%s%s%s' % (r'http://127.0.0.1:8000', settings.MEDIA_URL, str(obj.icon))
    

    第二种方法:@property

    	gender = serializers.CharField()
        # 第二种方法 在 models.py 的模型表中写 property装饰
        # 自定义序列化字段(插拔式,提倡使用)
        @property
        def gender(self):
            return self.get_sex_display()
    

    视图层

    many

    1)从数据库中将要序列化给前台的model对象,或是对个model对象查询出来
        user_obj = models.User.objects.get(pk=pk) 或者
        user_obj_list = models.User.objects.all()
    2)将对象交给序列化处理,产生序列化对象,如果序列化的是多个数据,要设置many=True
        user_ser = serializers.UserSerializer(user_obj, many=False) 或者
        user_ser = serializers.UserSerializer(user_obj_list, many=True)
    

    对象.data

    1)序列化 对象.data 就是可以返回给前台的序列化数据
        return Response(data={
            'status': 0,
            'msg': 0,
            'results': user_ser.data
        })
    

    view.py

    class User(APIView):
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if pk:
                try:
                    # 用户对象不能直接作为数据返回给前台
                    user_obj = models.User.objects.get(pk=pk)
                    # 序列化一下用户对象
                    user_ser = serializers.UserSerializer(user_obj)
                    # print(user_ser, type(user_ser))
                    return Response({
                        'status': 0,
                        'msg': 0,
                        'results': user_ser.data
                    })
                except:
                    return Response({
                        'status': 2,
                        'msg': '用户不存在',
                    })
            else:
                # 用户对象列表(queryset)不能直接作为数据返回给前台
                user_obj_list = models.User.objects.all()
                # 序列化一下用户对象
                user_ser_data = serializers.UserSerializer(user_obj_list, many=True).data
                return Response({
                    'status': 0,
                    'msg': 0,
                    'results': user_ser_data
                })
    

    3.反序列化

    1)设置必填与选填序列化字段,设置校验规则

    2)为需要额外校验的字段提供局部钩子函数,如果该字段不入库,且不参与全局钩子校验,可以将值取出校验

    3)为有联合关系的字段们提供全局钩子函数,如果某些字段不入库,可以将值取出校验

    4)重写create方法,完成校验通过的数据入库工作,得到新增的对象

    自定义serializer

    class UserDeserializer(serializers.Serializer):
        # 1) 哪些字段必须反序列化
        # 2) 字段都有哪些安全校验
        # 3) 哪些字段需要额外提供校验
        # 4) 哪些字段间存在联合校验
        # 注:反序列化字段都是用来入库的,不会出现自定义方法属性,会出现可以设置校验规则的自定义属性(re_pwd)
        name = serializers.CharField(
            max_length=64,
            min_length=3,
            error_messages={
                'max_length': '太长',
                'min_length': '太短'
            }
        )
        pwd = serializers.CharField()
        phone = serializers.CharField(required=False)
        sex = serializers.IntegerField(required=False)
    
        # 自定义有校验规则的反序列化字段
        re_pwd = serializers.CharField(required=True)
    

    validate_字段名 局部钩子

    	# 局部钩子:validate_要校验的字段名(self, 当前要校验字段的值)
        # 校验规则:校验通过返回原值,校验失败,抛出异常
        def validate_name(self, value):
            if 'g' in value.lower():  # 名字中不能出现g
                raise exceptions.ValidationError('名字非法,是个鸡贼!') 
            return value
    

    validate 全局钩子

        # 全局钩子:validate(self, 系统与局部钩子校验通过的所有数据)
        # 校验规则:校验通过返回原值,校验失败,抛出异常
        def validate(self, attrs):
            pwd = attrs.get('pwd')
            re_pwd = attrs.pop('re_pwd')
            if pwd != re_pwd:
                raise exceptions.ValidationError({'pwd&re_pwd': '两次密码不一致'})
            return attrs
    

    补充 :validators

    在字段中添加validators选项参数,也可以补充验证行为,如

    def about_django(value):
        if 'django' not in value.lower():
            raise serializers.ValidationError("图书不是关于Django的")
    
    class BookInfoSerializer(serializers.Serializer):
        btitle = serializers.CharField(label='名称', max_length=20, validators=[about_django])
    

    重写create方法

        # 要完成新增,需要自己重写 create 方法
        def create(self, validated_data):
            # 尽量在所有校验规则完毕之后,数据可以直接入库
            return models.User.objects.create(**validated_data)
    

    视图层

    book_ser = serializers.UserDeserializer(data=request_data) # 数据必须赋值data
    

    ser对象.is_valid() 参数: raise_exception=True

    book_ser.is_valid() # 结果为 通过 | 不通过
    is_valid() # 方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应
    

    ser对象.error & ser对象.validated_data

    is_valid() 不通过返回 book_ser.errors 给前台
    # 验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。
    
    验证成功,可以通过序列化器对象的validated_data属性获取数据
    

    ser对象.save()

    通过 book_ser.save() # 走得自定义 create 方法 得到新增的对象,再正常返回 
    

    view.py

    class User(APIView):
        # 只考虑单增
        def post(self, request, *args, **kwargs):
            # 请求数据
            request_data = request.data
            # 数据是否合法(增加对象需要一个字典数据)
            if not isinstance(request_data, dict) or request_data == {}:
                return Response({
                    'status': 1,
                    'msg': '数据有误',
                })
            # 数据类型合法,但数据内容不一定合法,需要校验数据,校验(参与反序列化)的数据需要赋值给data
            book_ser = serializers.UserDeserializer(data=request_data)
    
            # 序列化对象调用is_valid()完成校验,校验失败的失败信息都会被存储在 序列化对象.errors
            if book_ser.is_valid():
                # 校验通过,完成新增
                book_obj = book_ser.save()
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': serializers.UserSerializer(book_obj).data
                })
            else:
                # 校验失败
                return Response({
                    'status': 1,
                    'msg': book_ser.errors,
                })
    

    save()额外知识点

    1) 在对序列化器进行save()保存时,可以额外传递数据,这些数据可以在create()和update()中的validated_data参数获取到

    # request.user 是django中记录当前登录用户的模型对象
    serializer.save(owner=request.user)
    

    2)默认序列化器必须传递所有required的字段,否则会抛出验证异常。但是我们可以使用partial参数来允许部分字段更新

    # Update `comment` with partial data
    serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
    
  • 相关阅读:
    k8s如何删除处于terminating状态的ns资源
    js 可选链操作符
    Pod 配置,生命周期,调度
    cocos creator (二)
    cocos creator
    域名
    05月19日15:40:00 学习进度笔记
    springboot使用springsecuity
    05月19日16:51:53 学习进度笔记
    05月19日16:51:59 学习进度笔记
  • 原文地址:https://www.cnblogs.com/lddragon1/p/12113120.html
Copyright © 2020-2023  润新知