zoukankan      html  css  js  c++  java
  • drf的核心:序列化模块之Serializer和ModelSerializer(重点)模块

    drf的核心:序列化模块之Serializer和ModelSerializer(重点)模块

    • Serializer类(了解)- 偏底层,开发效率不高
    • ModelSerializer类(重中之重)- 开发运用阶段才有的序列化方式,开发效率高
    • ListModelSerializer类(正常)- 完成群增、群改接口的辅助序列化类

    导入序列化模块

    from rest_framework.serializers import Serializer, ModelSerializer, ListModelSerializer
    

    序列化与反序列化

    序列化: 将对象序列化成字符串用于传输

    反序列化: 将字符串反序列化成对象用于使用

    drf的序列化与反序列化

    序列化: 将Model类对象序列化成字符串用于传输

    反序列化: 将字符串反序列化成Model对象用于使用

    为什么要使用序列化

    后台的数据多以后台类的对象存在,经过序列化后,就可以格式化成能返回给前台的数据。

    Serializer

    序列化准备:

    • 模型层: models.py
    from django.db import models
    
    class User(models.Model):
        CHOICES_SEX = (
            (0, '男'),
            (1, '女')
        )
        name = models.CharField(max_length=64)
        age = models.IntegerField(default=0)
        height = models.DecimalField(max_digits=5, decimal_places=2, default=0)
        icon = models.ImageField(upload_to='icon', default='default.png')
        sex = models.IntegerField(choices=CHOICES_SEX, default=0)
    
        class Meta:
            db_table = 'ob_user'
            verbose_name = '用户表'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.name
    
    
    • 配置层: settings.py
    # 注册rest_framework
    INSTALLED_APPS = [
        # ...
        'rest_framework',
    ]
    
    # 配置数据库
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'db',
            'USER': 'root',
            'PASSWORD': 'root'
        }
    }
    
    # media资源
    MEDIA_URL = '/media/'  # 后期高级序列化类与视图类,会使用该配置
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')  # media资源路径
    
    # 国际化配置
    LANGUAGE_CODE = 'zh-hans'
    TIME_ZONE = 'Asia/Shanghai'
    USE_I18N = True
    USE_L10N = True
    USE_TZ = False
    
    • 项目名下的主路由: d-proj/urls.py
    from django.conf.urls import url, include
    from django.contrib import admin
    from django.views.static import serve
    from django.conf import settings
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
    	# 路由分发
    	url(r'^api/', include('api.urls')),
    	# 暴露静态文件
    	url('^media/(?P<path>.*)', serve, {'document_root':settings.MEDIA_ROOT})
    ]
    
    • 应用名下的子路由: api/urls.py
    from django.conf.urls import url
    from . import views
    urlpatterns = [
        # 序列化
        url(r'^v1/users/$', views.UserAPIView.as_view()),
        url(r'^v1/users/(?P<pk>d+)/$', views.UserAPIView.as_view()),
        # 反序列化
        url(r'^v2/users/$', views.UserV2APIView.as_view()),
        url(r'^v2/users/(?P<pk>d+)/$', views.UserV2APIView.as_view()),
    ]
    

    序列化使用:

    序列化提供给前台的字段个数由后台决定,可以少提供,

    但是提供的数据库对应的字段,名字一定要与数据库字段相同

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

    序列化组件 - 为每一个model类通过一套序列化工具类

    序列化组件的工作方式与django froms组件非常相似

    应用举例:

    • 应用名文件夹下创建serializers.py
    from rest_framework import serializers, exceptions
    from django.conf import settings
    from . import models
    
    from rest_framework import serializers
    class UserSerializer(serializers.Serializer):
        # model已有的属性字段
        #   如果要参与序列化,名字一定要与model的属性同名
        #   如果不参与序列化的model属性,在序列化类中不做声明
    
        name = serializers.CharField()
        age = serializers.IntegerField()
        height = serializers.DecimalField(max_digits=5, decimal_places=2)
        sex = serializers.IntegerField()
    
        # 自定义序列化字段,序列化的属性值由方法来提供,
        #   方法的名字:固定为 get_属性名,
        #   方法的参数:序列化对象,序列化的model对象
        #   强烈建议自定义序列化字段名不要与model已有的属性名重名
    
        gender = serializers.SerializerMethodField()
        def get_gender(self, obj):
            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))
    
    • 视图层: views.py

    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) 或者
    user_ser = serializers.UserSerializer(user_obj_list, many=True)
    注意: many会遍历user_obj_list,触发是一个具体对象,否则就需要制定many=True
    3)序列化 对象.data 就是可以返回给前台的序列化数据
    return Response({
    'status': 0,
    'msg': 0,
    'results': user_ser.data
    })

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from . import models, serializers
    from rest_framework import status
    class UserAPIView(APIView):
        # 单查群查
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
    
            if pk:
                # ORM操作数据库拿到资源数据
                # 格式化成能返回给前台的数据(序列化)
                # 返回格式化后的数据
                user_obj = models.User.objects.filter(pk=pk).first()
                if not user_obj:
                    return Response({
                        'status': 1,
                        'msg': '单查 error'
                    })
    
                # 完成序列化
                user_ser = serializers.UserSerializer(user_obj, many=False)
                user_data = user_ser.data
    
                return Response({
                    'status': 0,
                    'msg': '单查 ok',
                    'results': user_data
                })
    
            # 群查
            user_query = models.User.objects.all()
    
            # 完成序列化
            user_list_data = serializers.UserSerializer(user_query, many=True).data
            return Response({
                'status': 0,
                'msg': '群查 ok',
                'results': user_list_data
            })
    
    

    序列化总结

    一、视图类的三步操作

    • ORM操作数据库拿到资源数据
    • 格式化(序列化)成能返回给前台的数据
    • 返回格式化后的数据

    二、视图类的序列化操作

    • 直接将要序列化的数据传给序列化类
    • 要序列化的数据如果是单个对象,序列化的参数many为False;数据如果是多个对象(list,queryset),序列化的参数many为True。

    三、序列化类

    • model类中要反馈给前台的字段,在序列化类中要进行声明,属性名必须就是model的字段名,且Field类型也要保持一致(不需要明确规则)
    • model类中不需要反馈给前台的字段,在序列化类中不需要声明(省略)
    • 自定义序列化字段用 SerializerMethodField() 作为字段类型,该字段的值来源于 get_自定义字段名(self, obj) 方法的返回值

    反序列化使用

    • 在serializers.py文件中新建反序列化类
    class UserDeserializer(serializers.Serializer):
        # 系统校验规则
    
        # 系统必须反序列化的字段
        # 序列化属性名不是必须与model属性名对应,但是与之对应会方便序列化将校验通过的数据与数据库进行交互
        name = serializers.CharField(min_length=3, max_length=64, error_messages={
            'required': '姓名必填',
            'min_length': '太短',
        })
        pwd = serializers.CharField(min_length=3, max_length=64)
    
        # 系统可选的反序列化字段:没有提供不进行校验(数据库中有默认值或可以为空),提供了就进行校验
        age = serializers.IntegerField(min_value=0, max_value=150, required=False)
    
        # 自定义反序列化字段:一定参与校验,且要在校验过程中,将其从入库的数据中取出,剩余与model对应的数据才会入库
        re_pwd = serializers.CharField(min_length=3, max_length=64)
    
        # 自定义校验规则:局部钩子,全局钩子
        # 局部钩子:validate_字段名(self, 字段值)
        # 规则:成功返回value,失败抛异常
        def validate_name(self, value):
            if 'g' in value.lower():
                raise serializers.ValidationError('名字中不能有g')
            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)
    
    
    • 视图层: views.py

    基于上面的视图层,增加post请求方法

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from . import models, serializers
    from rest_framework import status
    class UserAPIView(APIView):
        # 单查群查
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
    
            if pk:
                # ORM操作数据库拿到资源数据
                # 格式化成能返回给前台的数据(序列化)
                # 返回格式化后的数据
                user_obj = models.User.objects.filter(pk=pk).first()
                if not user_obj:
                    return Response({
                        'status': 1,
                        'msg': '单查 error'
                    })
    
                # 完成序列化
                user_ser = serializers.UserSerializer(user_obj, many=False)
                user_data = user_ser.data
    
                return Response({
                    'status': 0,
                    'msg': '单查 ok',
                    'results': user_data
                })
    
            # 群查
            user_query = models.User.objects.all()
    
            # 完成序列化
            user_list_data = serializers.UserSerializer(user_query, many=True).data
            return Response({
                'status': 0,
                'msg': '群查 ok',
                'results': user_list_data
            })
    
        # 单增
        def post(self, request, *args, **kwargs):
            # 从请求对象中拿到前台的数据
            # 校验前台数据是否合法
            # 反序列化成后台Model对象与数据库交互
            request_data = request.data
    
            # 反序列化目的:封装数据的校验过程,以及数据库交互的过程
            user_ser = serializers.UserDeserializer(data=request_data)
            # 调用反序列化的校验规则:系统规则,自定义规则(局部钩子,全局钩子)
            result = user_ser.is_valid()
    
            if result:
                # 校验通过,可以与数据库进行交互:增(create),改(update)
                user_obj = user_ser.save()
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': serializers.UserSerializer(user_obj).data
                })
            else:
                # 校验失败,返回错误信息
                return Response({
                    'status': 1,
                    'msg': user_ser.errors
                }, status=status.HTTP_400_BAD_REQUEST)
    
    

    反序列化总结

    一、视图类的三步操作

    1)从请求对象中拿到前台的数据
    2)校验前台数据是否合法
    3)反序列化成后台Model对象与数据库交互

    二、视图类的反序列化操作
    1)将要反序列化的数据传给序列化类的data参数
    2)要反序列化的数据如果是单个字典,反序列化的参数many为False,数据如果是多个字典的列表,反序列化的参数many为True

    三、反序列化类

    1)系统的字段,可以在Field类型中设置系统校验规则(name=serializers.CharField(min_length=3))
    2)required校验规则绝对该字段是必校验还是可选校验字段(默认required为True,数据库字段有默认值或可以为空的字段required可以赋值为False)
    3)自定义的反序列字段,设置系统校验规则同系统字段,但是需要在自定义校验规则中(局部、全局钩子)将自定义反序列化字段取出(返回剩余的数据与数据库交互)
    4)局部钩子的方法命名 validate_属性名(self, 属性的value),校验规则为 成功返回属性的value 失败抛出校验错误的异常
    5)全局钩子的方法命名 validate(self, 所有属性attrs),校验规则为 成功返回attrs 失败抛出校验错误的异常

    ModelSerializer(重点)

    如果我们想要使用序列化器对应的是Django的模型类,drf为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。

    ModelSerializer与常规的Serializer相同,但提供了:

    • 基于模型类自动生成一系列字段
    • 基于模型类自动为Serializer生成validators,比如unique_together
    • 包含默认的create()和update()的实现

    ModelSerializer应用举例

    针对上面准备的用户表,从前台发送post请求,有可能发送用户密码,在模型类中要添加pwd字段

    • models.py
    from django.db import models
    
    class User(models.Model):
        CHOICES_SEX = ((0, '男'), (1, '女'))
        name = models.CharField(max_length=64)
        age = models.IntegerField(default=0)
        height = models.DecimalField(max_digits=5, decimal_places=2, default=0)
        icon = models.ImageField(upload_to='icon', default='default.png')
        sex = models.IntegerField(choices=CHOICES_SEX, default=0)
        
        # 已经迁移的数据库,新增字段需要允许为空或者设置默认值
        pwd = models.CharField(max_length=64, null=True)
    
        # 自定义插拔序列化字段:替换了在Serializer类中自定义的序列化字段(SerializerMethodField)
        # 自定义插拔序列化字段一定不参与反序列化过程
        @property
        def gender(self):
            return self.get_sex_display()
    
    
        class Meta:
            db_table = 'ob_user'
            verbose_name = '用户表'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.name
    
    
    • 在应用名文件夹下的serializers.py文件

    将上面serializer中的序列化与反序列化功能整合成一个类,这个类继承ModelSerializer

    from rest_framework.serializers import ModelSerializer
    from rest_framework import serializers
    from . import models
    # 整合序列化与反序列化
    class UserModelSerializer(ModelSerializer):
        # 自定义反序列化字段,校验规则只能在声明自定义反序列化字段时设置,且一定是write_only
        re_pwd = serializers.CharField(min_length=3, max_length=64, write_only=True)
    
        class Meta:
            # 将序列化类与Model类进行绑定
            model = models.User  
            # 设置序列化与反序列化所有字段(并划分序列化字段与反序列化字段)
            fields = ['name', 'age', 'height', 'gender', 'pwd', 're_pwd']
            # 用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,
                    'write_only': True,  # 只参与反序列化
                },
                'gender': {
                    'read_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已经帮我们重写了入库方法
    
    • views.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from . import models, serializers
    from rest_framework import status
    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
                })
    
            # 群查
            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)
    
    

    ModelSerializer单表序列化总结

    1)序列化与反序列功能可以整合成一个类,该类继承ModelSerializer
    2)继承ModelSerializer类的资源序列化类,内部包含三部分:Meta子类、局部钩子、全局钩子
    注:create和update方法ModelSerializer已经重写了,使用不需要重写
    3)在Meta子类中:
    用model来绑定关联的Model类
    用fields来设置所有的序列化反序列化字段
    用extra_kwargs来设置系统的校验规则
    4)重要的字段校验规则:
    read_only校验规则,代表该字段只参与序列化
    write_only校验规则,代表该字段只参与反序列化
    required校验规则,代表该字段在反序列化时是否是必填(True)还是选填(False),不能和read_only一起使用(规则冲突)
    规则细节:
    如果一个字段有默认值或是可以为空,没设置required规则,默认为False,反之默认值为True
    如果一个Model字段即没有设置read_only也没设置write_only,该字段默认参与序列化及反序列化
    5)自定义序列化字段:在Model类中,定义方法属性(可以返回特殊值,还可以完成连表操作),在序列化类的fields属性中可以选择性插拔,该字段只能序列化,只有models中有的字段才能反序列化
    6)自定义反序列化字段:在Serializer类中,自定义校验字段,校验规则也只能在声明字段时设置,自定义的反序列化字段,必须设置write_only为True

  • 相关阅读:
    VC++ MFC工程中中如何将一个工程的资源(如对话框)复制到另外一个工程
    VS调试_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));崩溃原因及解决方法
    使用libcurl下载文件小例
    [工具] 程序员必须软件
    [Linux] Linux软连接和硬链接
    [Android Pro] Android的Animation之LayoutAnimation使用方法
    [Java基础] java的守护线程与非守护线程
    [Linux] 守护进程和守护线程
    [Linux] linux文件系统学习
    [Java基础] Java线程复习笔记
  • 原文地址:https://www.cnblogs.com/zhuangyl23/p/11901839.html
Copyright © 2011-2022 走看看