zoukankan      html  css  js  c++  java
  • 序列化组件之ModelSerializer类

    序列化与反序列功能可以整合成一个类,该类继承ModelSerializer

    一、基于ModelSerializer类实现序列化器

    例如,我们以及有了一个数据库模型类User

    class User(models.Model):
        sex_choice = ((0,"男"),(1,"女"))
    
        name = models.CharField(max_length=32,unique=True)
        age = models.IntegerField(null=True)
        height = models.DecimalField(max_digits=5,decimal_places=2,null=True)
        sex = models.IntegerField(choices=sex_choice,default=0)
        icon = models.ImageField(upload_to="icon",default="default.png")
        pwd = models.CharField(max_length=32,null=True)
    

    1.1 实现序列化组件

    继承ModelSerializer类的资源序列化类,内部包含三部分

    • Meta子类、
    • 局部钩子、
    • 全局钩子
      注:create和update方法ModelSerializer已经重写了,使用不需要重写

    在Meta子类中

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

    重要的字段校验规则:

    • read_only,代表该字段只参与序列化
    • write_only,代表该字段只参与反序列化
    • required,代表该字段在反序列化是是否是必填(True)还是选填(False),不能和read_only一起使用(规则冲突)
    • 规则细节:
      如果一个字段有默认值或是可以为空,没设置required规则,默认为False,反之默认值为True
      如果一个Model字段即没有设置read_only也没设置write_only,该字段默认参与序列化及反序列化

    自定义序列化字段:

    • 在Model类中,定义方法属性(可以返回特殊值,还可以完成连表操作),在序列化类的fields属性中可以选择性插拔
    class User(models.Model):
    	# 自定义插拔序列化字段:替换了在Serializer类中自定义的序列化字段(SerializerMethodField)
        # 自定义插拔序列化字段一定不参与反序列化过程
        @property
        def gender(self):
            return self.get_sex_display()
    

    自定义反序列化字段:

    • 在类中,自定义校验字段,校验规则也只能在声明字段时设置,自定义的反序列化字段(如re_pwd)
    # 自定义反序列化字段,校验规则只能在声明自定义字段时设置,且一定是write_only
    re_pwd = serializers.CharField(min_length=3, max_length=64, write_only=True)
    
    '''serializers.py'''
    
    # 重点 ModelSerializer
    from rest_framework.serializers import ModelSerializer
    from .. import models
    # 整合序列化与反序列化
    class UserModelSerializer(ModelSerializer):
        # 将序列化类与Model类进行绑定
        # 设置序列化与反序列化所有字段(并划分序列化字段与反序列化字段)
        # 设置反序列化的局部与全局钩子
    
        # 自定义反序列化字段,校验规则只能在声明自定义字段时设置,且一定是write_only
        re_pwd = serializers.CharField(min_length=3, max_length=64, write_only=True)
    
        class Meta:
            # 用model来绑定关联的Model类
            model = models.User
            # 用fields来设置所有的序列化反序列化字段
            fields = ['name',"age","pwd","re_pwd","gender","sex"]
            # 用extra_kwargs来设置系统的校验规则,只有反序列化的时候能用到
            extra_kwargs = {
                "name":{
                    "required":True,
                    'min_length': 3,
                    'error_messages': {
                        'min_length': '太短'
                    }
                },
                "age": {
                    "required": True,  # 数据库有默认值或可以为空字段,required默认为False
                    'min_value': 0,
                },
                "pwd": {
                    "required": True,  # 数据库有默认值或可以为空字段,required默认为False
                    "write_only":True,
                },
                "gender": {
                    "read_only":True,
                },
                "sex": {
                    "write_only":True,
                },
            }
    
        def validate_name(self, value):
            if 'g' in value.lower():
                raise serializers.ValidationError('名字中不能有g')
            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
        # ModelSerializer已经帮我们重写了入库方法
    

    1.2 使用序列化组件

    '''views.py'''
    
    from . import models
    from .utils import serializers
    from rest_framework import status
    
    '''ModelSerializer类'''
    class UserV2APIView(APIView):
        def get(self,request,*args,**kwargs):
            pk = kwargs.get("pk")
            if pk:
                user_obj = models.User.objects.filter(pk=pk).first()
                if not user_obj:
                    return Response({
                        'status': 1,
                        'msg': '单查 error'
                    })
    
                # 完成序列化
                user_data = serializers.UserModelSerializer(user_obj, many=False).data
                return Response({
                    'status': 0,
                    'msg': '单查 ok',
                    'results': user_data
                })
            else:
                user_query = models.User.objects.all()
                # 完成序列化
                user_list_data = serializers.UserModelSerializer(user_query, many=True).data
                return Response({
                    'status': 0,
                    'msg': '群查 ok',
                    'results': user_list_data
                })
    
    
        def post(self,request,*args,**kwargs):
            request_data = request.data
    
            user_ser = serializers.UserModelSerializer(data=request_data)
    
            # 校验失败,直接抛异常,反馈异常信息给前台,只要校验通过,代码才会往下执行
            result = user_ser.is_valid()
            if result:
                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
                }, status=status.HTTP_400_BAD_REQUEST)
    
    

    1.3 视图类给序列化类传参

    视图类给序列化类传参只能在调用序列化类是通过content参数进行传参

    传递一个字典 key:value

    serializer = serializers.OrderModelSerializer(data=request.data, context={'request': request})
    

    1.4 通过source可以连接到数据库中的字段

    注:source属性只是序列化。

    如果在使用序列化类时传递了context={"request":request}会将ImageField的地址放在前面

    class Banner_ser(serializers.ModelSerializer):
        product_id = serializers.IntegerField(source="product.product_id")
        image_url = serializers.ImageField(source="image.image_url")
    
        class Meta:
            model = models.Banner
            fields = "__all__"
    

    二、连表序列化

    • 在对应的models表中,使用@property编写数据方法,即可在相应的序列化类中的fields实现插拔

    2.1 自定义插拔序列化方法属性

    '''models.py'''
    
    class Book(BaseModel):
        name = models.CharField(max_length=16)
        price = models.DecimalField(max_digits=9,decimal_places=2)
        # 设置外键字段,取反向查除的别名,断开连接,不设置级联删除
        publish = models.ForeignKey(
            to="Publish",related_name="books",db_constraint=False,
            on_delete=models.DO_NOTHING
        )
        # 重点:多对多外键实际在关系表中,ORM默认关系表中两个外键都是级联
        # ManyToManyField字段不提供设置on_delete,如果想设置关系表级联,只能手动定义关系表
        authors = models.ManyToManyField(
            to="Author",related_name="books",db_constraint=False
        )
    
        # 自定义连表深度,不需要反序列化,因为自定义插拔属性不参与反序列化
        @property
        def publish_name(self):
            return self.publish.name
        @property
        def author_info(self):
            temp_author_list = []
            for author in self.authors.all():
                temp_author_list.append({
                    "name": author.name,
                    "sex" : author.get_sex_display(),
                    "mobile": author.detail.mobile
                })
            return temp_author_list
    
    '''序列化类'''
    
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            # 序列化与反序列化整合
            fields = ['name', 'price', 'publish_name', 'author_info',"publish","authors"]
            extra_kwargs = {
                "publish":{
                    "write_only" : True
                },
                "authors":{
                    "write_only" : True
                }
            }
    

    2.2 连表序列化的其他知识点

    • fields = "__all__" 序列化反序列化所有字段
    • exclude = ('id',) 序列化反序列化除了id的所有字段, 就不用写fields了
    • depth = 1 显示fields字段中连表深度为1的字段,和fields配合使用
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            
            # fields = "__all__"  # 序列化反序列化所有字段, 就不用写fields了
            # exclude = ('id',)   # 序列化反序列化除了id的所有字段, 就不用写fields了
            
            depth = 1   # 显示fields字段中连表深度为1的字段,和fields配合使用
            fields = ['name', 'price', 'publish', 'authors']
    

    三、子序列化

    子序列化都是提供给外键(正向方向)完成深度查询的,外键数据是唯一:many=False;不唯一:many=True

    相对于子序列化组件来说的

    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            # 序列化与反序列化整合
            fields = ['name', 'price', 'publish_name', 'author_info',"publish","authors"]
            extra_kwargs = {
                "publish":{
                    "write_only" : True
                },
                "authors":{
                    "write_only" : True
                }
            }
    
    class PublishModelSerializer(serializers.ModelSerializer):
        books = BookModelSerializer(many=True)
        class Meta:
            model = models.Publish
            fields = ['name', 'address', 'books']
    

    四、基于序列化组件实现十大接口

    十大接口就是我们一开始在学习drf框架时,学到的接口规范,我们知道通过两条url路径就可以实现10个接口,完成10种不同的功能,不同的请求

    先来介绍一下,实现的是哪十大接口

    get请求
    	实现查看单条数据
    	实现查看所有数据
    	
    post请求
    	实现增加一条数据
    	实现增加多条数据
    	
    delete请求
    	实现删除一条数据
    	实现删除多条数据
    	
    put请求
    	实现整体将一条数据进行修改
    	实现整体将多条数据进行修改
    
    patch请求
    	实现将一条数据的局部数据修改
    	实现将多条数据的局部数据修改
    
    • (重点)单查群查接口:序列化类提供序列化对象,many参数控制着操作的数据是一条还是多条
    • (正常)单删群删接口:后台操作删除字段即可,前台提供pk就是单删,提供pks就是群删
    • (重点)单增群增接口:根据数据判断是单增还是群增,对应序列化类要设置many,而序列化类只需要通过data即可
    • (正常)单局部改:序列化类参数instance=修改的对象, data=修改的数据, partial=是否能局部修改,单整体修改就是partial=False(默认就是False)
    • (了解)群改:前台提供的数据,后台要转化成要修改的对象们和用来更新的数据们,ModelSerializer设置list_serializer_class关联自己的ListSerializer,重写update方法,完成群改

    4.1 设置关联自己的ListSerializer,重写ListSerializer的 update方法

    • 想要完成群改就要这么做
    '''
    ListSerializer实现了群增,但是没实现群改。
    ModelSerializer实现了单改,和单增。
    '''
    class BookListSerializer(serializers.ListSerializer):
        # 1、create方法父级ListSerializer已经提供了
    
        # 2、父级ListSerializer没有通过update方法的实现体,需要自己重写
        def update(self, instance, validated_data):
            print(instance)
            print(validated_data)
            '''
             child就是ModelSerializer类。实际上就是当一个个对象拿出来,去让ModelSerializer类帮你做更新
            '''
            return [
                self.child.update(instance[i], attrs) for i, attrs in enumerate(validated_data)
                
    class BookModelSerializer(serializers.ModelSerializer):
        # 通过BookModelSerializer.Meta.list_serializer_class来访问绑定的ListSerializer
        class Meta:
            # 关联ListSerializer完成群增群改
            list_serializer_class = BookListSerializer
    
  • 相关阅读:
    第6章 静态路由和动态路由(2)_路由汇总和默认路由
    第6章 静态路由和动态路由(1)_静态路由
    第5章 IP地址和子网划分(4)_超网合并网段
    第5章 IP地址和子网划分(3)_子网划分
    第5章 IP地址和子网划分(2)_IP地址分类和NAT技术
    第5章 IP地址和子网划分(1)_IP格式和子网掩码
    第4章 数据链路层(5)_高速以太网
    第4章 数据链路层(4)_扩展以太网
    第4章 数据链路层(3)_广播信道的数据链路
    第4章 数据链路层(2)_点到点信道的数据链路
  • 原文地址:https://www.cnblogs.com/XuChengNotes/p/11902095.html
Copyright © 2011-2022 走看看