zoukankan      html  css  js  c++  java
  • drf 序列化组件

    drf 序列化组件

    序列化图示

    单表序列化

    models.py

    from django.db import models
    from django.conf import settings
    class User(models.Model):
        SEX_CHOICES = ((0, ''), (1, ''))
        name = models.CharField(max_length=64, verbose_name='姓名')
        password = models.CharField(max_length=64)
        age = models.IntegerField()
        height = models.DecimalField(max_digits=5, decimal_places=2, default=0)
        sex = models.IntegerField(choices=SEX_CHOICES, default=0)
        # sex = models.CharField(choices=[('0', '男'), ('1', '女')])
        icon = models.ImageField(upload_to='icon', default='icon/default.png')
    
        # 自定义序列化给前台的字段
        # 优点:1)可以格式化数据库原有字段的数据 2)可以对外隐藏数据库字段名 3)可以直接连表操作
        @property  # 制造插头
        def gender(self):
            return self.get_sex_display()
    
        @property
        def img(self):
            return settings.BASE_URL + settings.MEDIA_URL + self.icon.name
    
        def __str__(self):
            return self.name

    serializers.py

    from rest_framework import serializers
    from . import models
    class UserModelSerializer(serializers.ModelSerializer):
       #配置类
    class Meta: # 该序列化类是辅助于那个Model类的 model = models.User # 设置参与序列化与反序列化字段 # 插拔式:可以任意选择字段返回给前台(插头都是在Model类中制造) # fields = ['name', 'age', 'height', 'sex', 'icon] fields = ['name', 'age', 'height', 'gender', 'img']

    views.py

    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    from . import models, serializers
    
    class UserAPIView(APIView):
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if pk:  # 单查
                # 1)数据库交互拿到资源obj或资源objs
                # 2)数据序列化成可以返回给前台的json数据
                # 3)将json数据返回给前台
                obj = models.User.objects.get(pk=pk)
                serializer = serializers.UserModelSerializer(obj, many=False)
                return Response(serializer.data)
    
            else:  # 群查
                # 1)数据库交互拿到资源obj或资源objs
                # 2)数据序列化成可以返回给前台的json数据
                # 3)将json数据返回给前台
                queryset = models.User.objects.all()
                # many操作的数据是否是多个
                serializer = serializers.UserModelSerializer(queryset, many=True)
                return Response(serializer.data)
    
        def post(self, request, *args, **kwargs):
            # 单增
            # 1)从请求request中获得前台提交的数据
            # 2)将数据转换成Model对象,并完成数据库入库操作
            # 3)将入库成功的对象列化成可以返回给前台的json数据(请求与响应数据不对等:请求需要提交密码,响应一定不展示密码)
            # 4)将json数据返回给前台
    
            return Response()

    内部类

    配置类

    # 概念:将类定义在一个类的内部,被定义的类就是内部类
    # 特点:内部类及内部类的所以名称空间,可以直接被外部类访问的
    # 应用:通过内部类的名称空间,给外部类额外拓展一些特殊的属性(配置),典型的Meta内部类 - 配置类
    
    class Book(model.Model):
        class Meta:
            db_model = "owen_book"  # 配置自定义表名
            
    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = "Book"  # 配置序列化类绑定的Model表

    序列化基类控制的初始化参数

    源码:

    class BaseSerializer(Field)
        def __init__(self, instance=None, data=empty, **kwargs):
            pass
    
    instance:是接收对象的 - 对象类型数据赋值给instance
    data:是接收数据的 - 请求来的数据赋值给data
    kwargs:内部有三个属性:many、partial、context
        many:操作的对象或数据,是单个的还是多个的
        partial:在修改需求时使用,可以将所有校验字段required校验规则设置为False
        context:用于视图类和序列化类直接传参使用

    常见使用

    # 单查接口
    UserModelSerializer(instance=user_obj)
    
    # 群查接口
    UserModelSerializer(instance=user_query, many=True)
    
    # 增接口
    UserModelSerializer(data=request.data)
    
    # 整体改接口
    UserModelSerializer(instance=user_obj, data=request.data)
    
    # 局部改接口
    UserModelSerializer(instance=user_obj, data=request.data, partial=True)
    
    # 删接口,用不到序列化类

    反序列化

    models.py

    class User(models.Model):
        SEX_CHOICES = ((0,''),(1,''))
        name = models.CharField(max_length=64,verbose_name='姓名')
        password = models.CharField(max_length=64)
        age = models.IntegerField()
        height = models.DecimalField(max_digits=5,decimal_places=2,default=0)
        sex = models.IntegerField(choices=SEX_CHOICES,default=0)
        # sex = models.CharField(choices=[('0','男'),('1','女')])
        icon = models.ImageField(upload_to='icon',default='icon/default.jpg')
    
        @property # 制造插头
        def gender(self):
            return self.get_sex_display()
    
        @property
        def img(self):
            return settings.BASE_URL + settings.MEDIA_URL + self.icon.name
    
        def __str__(self):
            return self.name

    views.py

    class UserAPIView(APIView):
        def post(self, request, *args, **kwargs):
            # 单增
            # 1)将前台请求的数据交给序列化类处理
            # 2)序列化类执行校验方法,对前台提交的所有数据进行数据校验:校验失败就是异常返回,成功才能继续
            # 3)序列化组件完成数据入库操作,得到入库对象
            # 4)响应结果给前台
            serializer = serializers.UserModelSerializer(data=request.data)
            if serializer.is_valid():
                # 校验成功 => 入库 => 正常响应
                obj = serializer.save()
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'result': '新增的那个对象'
                }, status=status.HTTP_201_CREATED)
            else:
                # 校验失败 => 异常响应
                return Response({
                    'status': 1,
                    'msg': serializer.errors,
                }, status=status.HTTP_400_BAD_REQUEST)

    serializers.py

    from rest_framework import serializers
    
    from . import models
    # 自定义serializers
    class UserModelSerializer(serializers.ModelSerializer):
        # 自定义反序列化字段(所有校验规则自己定义,也可以覆盖model已有的字段)
        # 覆盖model有的字段,不明确write_only会参与序列化过程
        password = serializers.CharField(min_length=3,max_length=8,write_only=True)  #覆盖
        # 自定义字段,不明确write_only序列化会报错,序列化会从model中强行反射自定义字段,但是model表中没有对应字段
        re_password = serializers.CharField(min_length=3,max_length=8,write_only=True)  #自定义
        class Meta:
            # 声明该序列化类是辅助于哪个Model类的
            model = models.User
            # 设置参与序列化与反序列化的字段
            # 插拔式,可以任意选择字段返回给前台(插头都是在models类中制造)
    
    
            # 反序列化第一波分析:
            # 1. name和age,在fields中标明了,且没有默认值,也没设置可以为空,入库时必须提供,所以校验时必须提供
            # 2. height,在fields中标明了,但有默认值,所以前台不提供 也能在入库时采用默认值(可以为空的字段同理)
            # 3. password,若没有在field中标明,则校验规则无法检测password情况,但是即使数据校验通过了
            #      也不能完成入库,原因是password是入库的必备条件
            # 4. gender和img是自定义插拔@property字段,默认不参与校验
    
            # 第二波分析:
            # 1)如何区分 序列化反序列化字段 | 只序列化字段(后台到前台) | 只反序列化字段(前台到后台)
            #       不做write_only和read_only任何限制 => 序列化反序列化字段
            #       只做read_only限制 => 只序列化字段(后台到前台)
            #       只做write_only限制 => 只反序列化字段(前台到后台)
    
            # 2)对前台到后台的数据,制定基础的校验规则(了解)
            #       CharField(max_length, min_length, errors_kwargs)
            #       DecimalField(min_value, max_value, max_digits, decimal_places,error_messages)
    
            # 3)如果一个字段有默认值或是可以为空,比如height,如何做限制
            #       虽然有默认值或是可以为空,能不能强制限制必须提供?可以,可通过required为True来限制
            #       如何让前台提交了该字段,我就校验,没提交我就不校验?1)required默认为False 2)有校验规则
    
            fields = ['name','age','height','password','gender','img','re_password']
    
            # 第三波分析
            # 1)制定的简易校验规则(没有制定)后,可以再通过字段的 局部钩子 对该字段进行复杂校验
            # 2)每个字段进行逐一复杂校验后,还可以进行集体的 全局钩子 校验
            #       涉及对自定义反序列化字段的校验:re_password(要参与校验,但是不会入库)
    
            # 校验规则
            extra_kwargs = {
                'name':{
                    # 'write_only':True
                    # 'read_only':True
                },
                'password':{
                    'write_only':True,
                    'min_length':3,
                    'max_length':8,
                    'error_messages':{
                        'min_length':'太短',
                        'max_length':'太长'
                    },
                },
                'height':{
                    # 'required':True,
                    'min_value':0,
                }
            }

    总结:

    """
    标注:序列化 => 后台到前台(读)  |  反序列化 => 前台到后台(写)
    1)不管是序列化还是反序列化字段,都必须在fields中进行声明,没有声明的不会参与任何过程(数据都会被丢弃)
    
    2)用 read_only 表示只读,用 write_only 表示只写,不标注两者,代表可读可写
    
    3)
    i)自定义只读字段,在model类中用@property声明,默认就是read_only
    @property
    def gender(self):
        return self.get_sex_display()
        
    ii)自定义只写字段,在serializer类中声明,必须手动明确write_only
    re_password = serializers.CharField(write_only=True)
    
    特殊)在serializer类中声明,没有明确write_only,是对model原有字段的覆盖,且可读可写
    password = serializers.CharField()
    
    4)用 extra_kwargs 来为 写字段 制定基础校验规则(了解)
    
    5)每一个 写字段 都可以用局部钩子 validate_字段(self, value) 方法来自定义校验规则,成功返回value,失败抛出 exceptions.ValidationError('异常信息') 异常
    
    6)需要联合校验的 写字段们,用 validate(self, attrs) 方法来自定义校验规则,,成功返回attrs,失败抛出 exceptions.ValidationError({'异常字段': '异常信息'}) 异常
    
    7)extra_kwargs中重要的限制条件
         'required':代表是否必须参与写操作,有默认值或可以为空的字段,该值为False;反之该值为True;也可以手动修改值
    """
    
    """
    开发流程:
    1)在model类中自定义 读字段,在serializer类中自定义 写字段
    2)将model自带字段和所有自定义字段书写在fields中,用write_only和read_only区别model自带字段
    3)可以写基础校验规则,也可以省略
    4)制定局部及全局钩子
    """

    全局和局部钩子

    局部、全局钩子,是和Meta同缩进的,属于序列化类的

    局部钩子:validate_要校验的字段(self.字段的值)

    全局钩子:validate(self,所有字段值的字典)

    校验规则:成功返回传来的数据,失败抛出异常

    注意:必须在其前面所有校验规则全通过才能走全局钩子校验

    局部钩子

    class UserModelserializer(serializers.Serializer):
        .......
        def validate_字段名称(self,value):
            if 情况不满足:
                raise serializers.ValidationError('异常信息')  #抛出异常
            else:
                return value

    全局钩子

    class UserModelserializer(serializers.Serializer):
        # print(attrs)
        password = attrs.get('password')  # 只是拿出来校验
        re_password = attrs.pop('re_password')  # 必须取出校验,因为不能入库
    
        if password != re_password:
            raise exceptions.ValidationError({'re_password': '两次密码不一致'})
        else:
            return attrs

    补充知识点:

    断言

    序列化类外键字段的覆盖

    """
    1)在序列化类中自定义字段,名字与model类中属性名一致,就称之为覆盖操作
        (覆盖的是属性的所有规则:extra_kwargs中指定的简易规则、model字段提供的默认规则、数据库唯一约束等哪些规则)
    
    2)外键覆盖字段用PrimaryKeyRelatedField来实现,可以做到只读、只写、可读可写三种形式
        只读:read_only=True
        只写:queryset=关联表的queryset, write_only=True
        可读可写:queryset=关联表的queryset
        
    3)当外界关联的数据是多个时,需标识many=True条件
    """

    如:

    class BookModelSerializer(serializers.ModelSerializer):
        # 如何覆盖外键字段
        # publish = serializers.PrimaryKeyRelatedField(read_only=True)  # 只读
        # publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all(), write_only=True)  # 只写
    
        publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all())  # 可读可写
        authors = serializers.PrimaryKeyRelatedField(queryset=models.Author.objects.all(), many=True)    
    
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'image', 'publish', 'authors')

     小结:

    """
    序列化组件
        i)视图类中使用序列化
        serializer = UserModelSerializer(
            instance="对象(们)",
            data="数据(们)",
            many=False|True,  # 与数据或对象配套,代表操作的是否是多个
            partial=False|True,  # 运用在局部修改中,所以校验可以选填(required=False)
            context={'request': request}  # 视图、序列化传参 
        )
        serializer.is_valid(raise_exception=False|True)  # 校验
        serializer.save()  # 入库
        serializer.data  # 序列化后的数据
        serializer.errors  # 校验失败的信息
        
        ii)Meta配置类中的配置
        model:关联的Model类
        fields:所以序列化与反序列化字段
        extra_kwargs:简单的校验规则
        exclude:除某些字段
        depth:连表深度
        
        iii)自定义校验规则
        validate_字段名:局部校验钩子
        validate:全局校验钩子
        
        iv)入库方法
        create:增数据入库
        update:改数据入库
        后期这两个方法可能会被重写:涉及一些字段加密解密处理、不仅仅是单表入库操作
                user = User.create()
                UserDetail.create(user_id=user.id)
        
        v)自定义字段
        @property:在model类中自定义序列化字段
        自定义字段 = serializers.字段类型(write_only=True, 其他规则):在serializer类中的自定义反序列化字段
        系统字段 = serializers.字段类型(规则):覆盖字段,可以设置为只读、只写、可读可写
                外键字段的字段类型:PrimaryKeyRalatedField
        
        vi)如果有群改操作
            自定义ListSerializer子类,重写update方法
            在相关ModelSerializer中用list_serializer_class配置进行关联
    """
  • 相关阅读:
    【BZOJ】1049: [HAOI2006]数字序列(lis+特殊的技巧)
    【BZOJ】1089: [SCOI2003]严格n元树(递推+高精度/fft)
    【BZOJ】1070: [SCOI2007]修车(费用流+特殊的技巧)
    【BZOJ】1014: [JSOI2008]火星人prefix(splay+hash+二分+lcp)
    【BZOJ】1090: [SCOI2003]字符串折叠(dp)
    【BZOJ】1189: [HNOI2007]紧急疏散evacuate(二分+bfs+网络流)
    【BZOJ】3709: [PA2014]Bohater(贪心)
    【BZOJ】2929: [Poi1999]洞穴攀行(最大流)
    【BZOJ】2435: [Noi2011]道路修建(树形dp)
    【BZOJ】1458: 士兵占领(上下界网络流)
  • 原文地址:https://www.cnblogs.com/baohanblog/p/12327642.html
Copyright © 2011-2022 走看看