• drf 序列化


    drf 序列化

    什么是序列化与反序列化

    • 序列化:就是把对象转化为可以进行网络传输的数据(程序语言转换为JSON/XML)
    • 反序列化:就是把网络传输的数据转化为对象进行使用(JSON/XML转换为程序语言)

    序列化的作用

    1. 进行数据的校验
    2. 对数据对象进行转换

    简单认识序列化

    首先来进行准备工作:

    from django.db import models
    
    class User(models.Model):
        # choices的字段,直接获取只能获取 0 | 1 | 2
        # 想获取 值后 的映射关系 男 | 女 | 哇塞 用  get_字段名_display()
        # eg:user_obj.get_sex_diaplay()
        SEX_CHOICES = [
            (0, '男'),
            (1, '女'),
            (2, '哇塞')
        ]
        username = models.CharField(max_length=64, unique=True)
        password = models.CharField(max_length=64)
        sex = models.IntegerField(choices=SEX_CHOICES, default=0)
        icon = models.ImageField(upload_to='icon', default='icon/default.png')
        create_time = models.DateTimeField(auto_now_add=True, null=True)
        is_delete = models.BooleanField(default=False)
    
        class Meta:
            db_table = 'old_boy_user'
            verbose_name = '用户'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.username
    

    自定义序列化类

    定义

    通过继承rest_framework.serializers中的Serializer类进行定义,但需要有如下几个注意点:

    1. 参与序列化的属性名必须与model类的属性字段名相同
    2. 不需要序列化的属性字段在序列化类中不需要声明
    3. 方法序列化字段,不需要在model类中有字段属性对应,值来源于get_字段方法,序列化字段可以完全自定义,也可以与model类中有字段属性相同

    如:

    from rest_framework import serializers
    from django.conf import settings
    from . import models
    # 序列化
    class UserSerializer(serializers.Serializer):
        username = serializers.CharField()
        
        # 不需要序列化的不需要声明
        # password = serializers.CharField()
        
        sex = serializers.IntegerField()   # 只能返回数字
    	# 自定义序列化字段
        gender = serializers.SerializerMethodField()
        def get_gender(self, user_obj):
            return user_obj.get_sex_display()    # 返回性别
        
        # icon = serializers.ImageField()
        # 二次资源需要返回完整的url链接
        icon = serializers.SerializerMethodField()
        def get_icon(self, user_obj):
            icon_url = 'http://127.0.0.1:8000{}{}'.format(settings.MEDIA_URL, user_obj.icon)
            return icon_url
    

    常用字段类型

    字段 字段构造方式
    BooleanField BooleanField()
    NullBooleanField NullBooleanField()
    CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
    EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
    RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
    SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
    URLField URLField(max_length=200, min_length=None, allow_blank=False)
    UUIDField UUIDField(format='hex_verbose') format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
    IPAddressField IPAddressField(protocol='both', unpack_ipv4=False, **options)
    IntegerField IntegerField(max_value=None, min_value=None)
    FloatField FloatField(max_value=None, min_value=None)
    DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
    DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
    DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
    TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
    DurationField DurationField()
    ChoiceField ChoiceField(choices) choices与Django的用法相同
    MultipleChoiceField MultipleChoiceField(choices)
    FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ListField ListField(child=<a_field_instance>, min_length=None, max_length=None)
    DictField DictField(child=<a_field_instance>)

    选项参数

    参数名称 作用
    max_length 最大长度
    min_lenght 最小长度
    allow_blank 是否允许为空
    trim_whitespace 是否截断空白字符
    max_value 最小值
    min_value 最大值

    通用参数

    参数名称 说明
    read_only 表明该字段仅用于序列化输出,默认False
    write_only 表明该字段仅用于反序列化输入,默认False
    required 表明该字段在反序列化时必须输入,默认True
    default 反序列化时使用的默认值
    allow_null 表明该字段是否允许传入None,默认False
    validators 该字段使用的验证器
    error_messages 包含错误编号与错误信息的字典
    label 用于HTML展示API页面时,显示的字段名称
    help_text 用于HTML展示API页面时,显示的字段帮助提示信息

    使用自定义序列化对象

    定义好Serializer类后,就可以创建Serializer对象了。

    Serializer的构造方法为:

    Serializer(instance=None, data=empty, **kwargs)
    

    说明:

    1. 用于序列化时,将模型类对象传入instance参数,当序列化数据为单列集合时,此时many=True必须明确

      补充:单列集合就是具有索引的集合,例如列表,元组,集合,django中的 QuerySet

    2. 除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

      serializer = UserSerializer(account, context={'request': request})
      

      通过context参数附加的数据,可以通过Serializer对象的context属性获取。

    案例

    from rest_framework.views import APIView
    from utils_home.response import APIResponse
    from . import models, serializers
    class UserAPIView(APIView):
        # 序列化对象:
        # UserSerializer(序列化数据, many)
        # 1)序列化数据可以为User类的单个对象,many=False(默认,可以省略不写)
        # 2)序列化数据可以为状态User类的多个对象的单列集合
        #       [] () {} QuerySet,此时many=True必须明确
        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 APIResponse(1, 'pk error')
                user_obj_data = serializers.UserSerializer(user_obj).data
                return APIResponse(0, 'ok', results=user_obj_data)
    
            # 获取多个资源
            user_query = models.User.objects.all()
            user_ser = serializers.UserSerializer(user_query, many=True)
            user_list_data = user_ser.data
            return APIResponse(0, 'get ok', results=user_list_data)
    

    简单认识反序列化

    自定义反序列化类

    同样通过继承rest_framework.serializers中的Serializer类进行定义,但需要有如下几个注意点:

    1. 在字段类型后定义基础校验规则
    2. 基础校验完毕,可以在局部钩子方法中进行单个字段校验规则的扩展
    3. 在局部钩子校验完毕,在全局钩子中完成多个字段协同校验
    4. 如果需要提供数据的增加或修改功能,需要重写create或update方法,完成数据的入库

    如:

    from rest_framework import serializers
    from django.conf import settings
    from . import models
    # 反序列化
    class UserDeserializer(serializers.Serializer):
        # 1)默认校验
        username = serializers.CharField(
            min_length=3,
            error_messages={
                'min_length': '用户名太短'
            }
        )
        password = serializers.CharField(
            min_length=3,
            error_messages={
                'min_length': '密码太短'
            }
        )
        re_password = serializers.CharField(
            min_length=3,
            required=True,
            error_messages={
                'min_length': '确认密码太短',
                'required': '确认密码不能为空'
            }
        )
    
        # 有默认值的字段,前台可能提供,可能提供,这样的字段用required=False处理
        sex = serializers.IntegerField(required=False)
    
        # 2)局部钩子: validate_字段名(self, 字段值)
        def validate_username(self, value):
            if 'sb' in value:
                raise serializers.ValidationError('用户名包含敏感词汇')
            return value
    
        # 3)全局钩子:validate(self, 所有字段值)
        def validate(self, attrs):
            print(attrs)
            password = attrs.get('password')
            # 校验通过后,要完成数据的增加,该增加不参与,要从数据们中剔除
            re_password = attrs.pop('re_password')
            if password != re_password:
                raise serializers.ValidationError({'re_password': '两次密码不一致'})
            return attrs
    
        # 4)完成model类对象的增加,必须重写create方法
        def create(self, validated_data):
            try:
                return models.User.objects.create(**validated_data)
            except:
                raise IOError('数据库入库失败')  # 数据库异常在异常模块中完善
    
    

    使用自定义反序列化对象

    说明:

    1. 反序列化数据可以为单个数据字典,many=False或者不写

    2. 用于反序列化时,将要被反序列化的数据传入data参数,当序列化数据为单列集合时,此时many=True必须明确

    3. 反序列化数据必须赋值给data关键字参数,才能进行数据校验

    4. 序列化对象.is_valid(raise_exception=True)校验失败会自动返回错误信息给前台

    5. if 序列化对象.is_valid(): 可以自定义校验成功与失败分支的返回结果

      成功可以完成新增或修改,失败错误信息在 序列化对象.errors 中

    from rest_framework.views import APIView
    from utils_home.response import APIResponse
    from . import models, serializers
    class UserAPIView(APIView):
        # 反序列化请求数据
        def post(self, request, *args, **kwargs):
            request_data = request.data
            print(request_data)
            user_ser = serializers.UserDeserializer(data=request_data)
            # 自动返回错误信息
            # user_ser.is_valid(raise_exception=True)
            if user_ser.is_valid():  # 自定义处理校验成功的逻辑
                user_obj = user_ser.save()
                return APIResponse(0, 'ok',
                    results=serializers.UserSerializer(user_obj).data
                )
            else:  # 自定义返回错误信息
                return APIResponse(1, 'failed', results=user_ser.errors)
    

    序列化器(序列化与反序列化整合)

    自定义序列化器

    说明:

    1. 参与序列化与反序列化的字段(包括自定义字段)都必须明确

    2. 通过 read_only=True 表明该字段只参与序列化

    3. 通过 write_only=True 表明该字段只参与反序列化

    4. 参与反序列化,但是是选填字段(前台可以提供或不提供),用 required=False 处理

    5. 要完成数据库增加,需要重写 create 方法

    6. 要完成数据库更新,需要重写 update 方法

      • instance参数传入的是自定义传入的要更新的原数据(pk | obj | queryset)
      • validated_data:是校验通过后的新数据
    from rest_framework import serializers
    from django.conf import settings
    from . import models
    # 序列化与反序列化整合
    class UserV2Serializer(serializers.Serializer):    
        username = serializers.CharField(min_length=3)
        # 只参与反序列化 write_only=True
        password = serializers.CharField(write_only=True, min_length=3)
        sex = serializers.IntegerField(write_only=True, required=False)
        re_password = serializers.CharField(write_only=True, min_length=3, required=True)
        # 只参与序列化 read_only=True
        gender = serializers.SerializerMethodField(read_only=True)
        def get_gender(self, user_obj):
            return user_obj.get_sex_display()
        icon = serializers.SerializerMethodField(read_only=True)
        def get_icon(self, user_obj):
            icon_url = 'http://127.0.0.1:8000{}{}'.format(settings.MEDIA_URL, user_obj.icon)
            return icon_url
    
        def validate_username(self, value):
            if 'sb' in value:
                raise serializers.ValidationError('用户名包含敏感词汇')
            return value
    
        def validate(self, attrs):
            print('序列化内:', self.context)
            password = attrs.get('password')
            re_password = attrs.get('re_password')
            if password:
                if re_password:
                    attrs.pop('re_password')
                    if password != re_password:
                        raise serializers.ValidationError({'re_password': '两次密码不一致'})
                else:
                    raise serializers.ValidationError({'re_password': '密码必须确认'})
            return attrs
    
        # 增
        def create(self, validated_data):
            try:
                return models.User.objects.create(**validated_data)
            except:
                raise IOError('数据库入库失败')
        # 改
        def update(self, instance, validated_data):
            # instance的值外部反序列化传入要更新的自定义标识决定
            instance.update(**validated_data)
            return instance.first()
    

    使用序列化器完成五大接口

    from rest_framework.views import APIView
    from utils_home.response import APIResponse
    from . import models, serializers
    class UserV2APIView(APIView):
        # 单取、群取
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            # 单取
            if pk:
                user_obj = models.User.objects.filter(pk=pk, is_delete=False).first()
                if not user_obj:
                    return APIResponse(1, 'pk error')
                user_obj_data = serializers.UserV2Serializer(user_obj).data
                return APIResponse(0, 'ok', results=user_obj_data)
            
    		# 群取
            user_query = models.User.objects.filter(is_delete=False).all()
            user_ser = serializers.UserV2Serializer(user_query, many=True)
            user_list_data = user_ser.data
            return APIResponse(0, 'ok', results=user_list_data)
    
        # 单增
        def post(self, request, *args, **kwargs):
            request_data = request.data
            print(request_data)
            user_ser = serializers.UserV2Serializer(data=request_data)
            if user_ser.is_valid():
                user_obj = user_ser.save()
                return APIResponse(0, 'ok',
                    results=serializers.UserV2Serializer(user_obj).data
                )
            else:
                return APIResponse(1, 'failed', results=user_ser.errors)
    
        # 单整体改
        def put(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if not pk:
                return APIResponse(1, 'pk error')
    
            user_query = models.User.objects.filter(pk=pk, is_delete=False)
            if not user_query:
                return APIResponse(1, 'user error')
    
            # 第一种:user_query完成数据的更新
            # user_query = models.User.objects.filter(pk=pk)
            # user_query.update(**kwargs)
    
            # 第二种:user_obj完成数据的更新
            # user_obj = models.User.objects.filter(pk=pk).first()  # type: models.User
            # user_obj.username = 'new_username'
            # ...
            # user_obj.save()
    
    
            request_data = request.data
            user_ser = serializers.UserV2Serializer(instance=user_query, data=request_data)
            if user_ser.is_valid():
                # save的返回值是由update内部自定义的返回值决定
                user_obj = user_ser.save()
                return APIResponse(0, 'ok',
                    results=serializers.UserV2Serializer(user_obj).data
                )
            else:
                return APIResponse(1, 'failed', user_ser.errors)
    
        # 单局部改
        def patch(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if not pk:
                return APIResponse(1, 'pk error')
            user_query = models.User.objects.filter(pk=pk, is_delete=False)
            if not user_query:
                return APIResponse(1, 'user error')
            request_data = request.data
            # 局部数据修改,设置 partial=True
            # 视图类给序列化类传递自定义参数 context=值 值一般用字典
            #       在序列化类的钩子函数中用self.context获取传入的值
            user_ser = serializers.UserV2Serializer(context={'arg': '我是视图的'}, partial=True, instance=user_query, data=request_data)
            if user_ser.is_valid():
                user_obj = user_ser.save()
                return APIResponse(0, 'ok',
                	results=serializers.UserV2Serializer(user_obj).data
                 )
            else:
                return APIResponse(1, 'failed', user_ser.errors)
    
        # 单删
        def delete(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if not pk:
                return APIResponse(1, 'pk error')
            user_obj = models.User.objects.filter(pk=pk, is_delete=False).first()
            if not user_obj:
                return APIResponse(1, '删除失败')
            user_obj.is_delete = True
            user_obj.save()
            return APIResponse(0, '删除成功')
    
  • 相关阅读:
    一行JS代码实现的滑动门
    一款JS+CSS打造绝对经典的资讯网站滑动门特效
    【荐】JS封装的一个无限级的下拉树形菜单
    JS+CSS实现漂亮实用的红色导航菜单
    JS+CSS仿网易的选项卡TAB标签样式
    JS+CSS实现的不规则TAB选项卡效果
    jQuery仿FLASH响应鼠标滚动的动感菜单
    纯CSS仿迅雷首页的菜单导航代码
    JS+CSS仿雅虎首页网站快捷入口的切换效果
    【荐】Jquery+CSS打造的泡沫弹出框式的侧边蓝色导航菜单
  • 原文地址:https://www.cnblogs.com/Hades123/p/11461312.html
Copyright © 2020-2023  润新知