• drf序列化器之反序列化的数据验证


    使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。

    在获取反序列化的客户端数据前,必须在视图中调用序列化对象的is_valid()方法,序列化器内部是在is_valid方法内部调用验证选项和验证方法进行验证,验证成功返回True,否则返回False。

    验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误提示。如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。

    验证成功,可以通过序列化器对象的validated_data属性获取数据。

    在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为。

    1、准备工作

    注册一个图书app及图书表模型

    python manage.py startapp unsers
    

    在配置文件setting.py中注册子应用

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rest_framework',  # 把drf框架注册到django项目中
        'unsers',  # 演示反序列化
    ]
    

    注释csrf校验,因为提交数据涉及到post方法提交数据,把settings.py中的中间件的csrf暂时关闭

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        # 'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    

    创建表模型

    from django.db import models
    
    
    # Create your models here.
    class BookInfo(models.Model):
        """图书信息"""
        title = models.CharField(max_length=20, verbose_name='标题')
        pub_date = models.DateField(verbose_name='发布日期')
        # 设置存储文件的子目录为avatar,总目录不写的话是在settings中配置,不填则没有
        image = models.ImageField(upload_to="avatar", verbose_name='图书封面')
        price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="价格")
        read = models.IntegerField(verbose_name='阅读量')
        comment = models.IntegerField(verbose_name='评论量')
    
        class Meta:
            # db_table = "表名"
            db_table = "tb_book_info"
            verbose_name = "图书"
            verbose_name_plural = verbose_name
    

    注意:因为当前模型中, 设置到图片上传处理,运行起来后会有提示,所以需要安装PIL

    pip3 install Pillow
    

    执行数据迁移

    python3 manage.py makemigrations
    python3 manage.py migrate
    

    2、字段验证

    经过上面的准备工作,接下来就可以给图书信息增加图书的功能,需要对来自客户端的数据进行处理,例如,验证和保存到数据库中。此时,就可以使用序列化器的反序列化器,接下来,定义一个图书的序列化器,此序列化器主要用于反序列化器阶段,在unsers子应用,创建serializers.py,代码如下

    from rest_framework import serializers
    
    class BookInfoSerializer(serializers.Serializer):
        # 这里声明的字段用于进行反序列化器
        # 字段名 = serializers.字段类型(验证选项)
        # read_only=True,设置id为只读字段,当字段设置为read_only为True,则当前字段只会在序列化阶段使用
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField(required=True, min_length=1, max_length=20, label="标题", help_text="标题", error_messages={
            "required": "标题不能为空!",
            "max_length": "标题不能超过6个字符",
        })
        # required=True 当前字段必填
        # write_only=True 表示当前字段只会在反序列化阶段使用,客户端提交数据的时候使用,不会提供给客户端
        pub_date = serializers.DateField(required=True,label="发布日期", help_text="发布日期")
        price = serializers.DecimalField(max_digits=8, decimal_places=2, required=True, label="价格", help_text="价格")
        read  = serializers.IntegerField(min_value=0, default=0, label="阅读量", help_text="阅读量")
        comment = serializers.IntegerField(min_value=0, default=0, label="评论量", help_text="评论量")
    

    通过构造序列化器对象,并将要反序列化的数据传递给data构造参数,进而进行验证,编写视图类如下

    # Create your views here.
    from django.views import View
    from .models import BookInfo
    from django.http.response import JsonResponse
    from .serializers import BookInfoSerializer
    
    
    class BookInfoView(View):
        def post(self, request):
            """反序列化,验证和添加数据"""
            # 接收并实例化序列化器对象
            serializer = BookInfoSerializer(data=request.POST)
            # 启动验证
            # is_valid 有个可选参数raise_exception,用于显示序列化器抛出的异常,直接终止视图代码的执行
            # 如果设置了raise_exception=True,则下面的18~21行代码,就不要开发者自己编写,系统会自动根据请求的方式自动返回错误给客户端。
            # 如果是ajax请求,则自动返回json格式的错误信息
            # 如果是表单请求,则自动返回html格式的错误信息
            result = serializer.is_valid()
            # result = serializer.is_valid(raise_exception=True)
            print(result)  # 验证结果,True表示验证通过了,开发时一般不需要接收
            if not result:
                # 当验证失败,则错误信息属性就有内容
                print(serializer.errors)
                return JsonResponse(serializer.errors)
            else:
                # 获取验证完成后的客户端数据 如果验证失败,则vcalidated_data是空字典
                print(serializer.validated_data)
                # 把数据保存到数据库中
                instance = BookInfo.objects.create(**serializer.validated_data)
                # instance = serializer.create(serializer.validated_data)
                # 序列化器实例化时,如果有save参数,则save相当于update,否则就是create
                # instance = serializer.save()
                # 返回结果,也是需要使用序列化进行转换的
                serializer = BookInfoSerializer(instance=instance)
                return JsonResponse(serializer.data)
    

    注册url

    from django.urls import path
    from . import views
    urlpatterns = [
        path("books/", views.BookInfoView.as_view()),
    ]
    

    利用postman测试向此接口提交数据

    此时查看数据库中的记录,已经成功被写入

    3、validate_字段名验证

    <field_name>字段进行验证,在序列化器中编写如下内容:

    def validate_title(self, data):
        # 验证单个字段时,方法名必须固定为validate_字段,这里的data代表的就是字段值,
        if "测试" in data:
            """抛出异常"""
            raise serializers.ValidationError("对不起,当前标题不能出现关键字")
    
        # 验证方法必须要有返回值,这里的返回值将会被填写到 serailzier对象的validated_data里面
        return data  # 验证通过以后,必须要返回验证的结果数据,否则序列化器的validated_data无法得到当前字段的结果
    

    利用postman测试向此接口提交数据

    4、validate验证

    在序列化器中需要同时对多个字段进行比较验证时,可以定义validate方法来验证

    def validate(self, data):
        """验证多个字段时,方法名必须为validate,
        参数data代表了所有字段的数据值,其实就是视图代码中实例化序列化器对象时的data参数
        开发中,类似 密码和确认密码,此时这2个字段,必须进行比较才能通过验证
        """
        print(data)
        # 例如,我们要求图书的评论必须比阅读量要少
        read = data.get("read")
        comment = data.get("comment")
        if read < comment:
            raise serializers.ValidationError("对不起,阅读量必须比评论量大")
    
        # 验证密码和确认密码
        # 验证方法必须要有返回值
        return data
    

    利用postman测试向此接口提交数据

    5、validators验证器验证

    验证器类似于验证方法,但是验证方法只属于当前序列化器,如果有多个序列化器共用同样的验证功能,则可以把验证代码分离到序列化器外部,作为一个普通函数,由validators加载到序列化器中使用。

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

    # 在序列化器的外面声明一个验证函数
    def check_price(data):  # data代表要验证的数据
        if data < 0:
            raise serializers.ValidationError("对不起,价格不能出现负数")
        # 验证函数也必须把数据返回
        return data
        
    ...
    class BookInfoSerializer(serializers.Serializer):
    ...
        # 调用验证器validators,这里的参数是一个列表,列表的成员是函数,函数名不能加引号
        # price = serializers.DecimalField(required=True, max_digits=8, decimal_places=2)
        price = serializers.DecimalField(required=True, max_digits=8, decimal_places=2, validators=[check_price])
    

    利用postman测试向此接口提交数据

    6、小结

    is_valid实际上内部执行了三种不同的验证方式:

    • 先执行了字段内置的验证选项
    • 在执行了validators自定义选项
    • 最后执行了validate自定义验证方法[包含了validate_<字段>, validate]

    附:常用字段和参数

    常用字段类型

    字段 字段构造方式
    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=, min_length=None, max_length=None)
    DictField DictField(child=)

    选项参数:

    参数名称 作用
    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页面时,显示的字段帮助提示信息
  • 相关阅读:
    Hessian 服务端流程
    JSH面试感悟
    hibernate的update() 更新延迟或者无法更新,导致同个service调用存储过程执行方法不精确
    一个变量名引发的血案
    oracle for loop循环以及游标循环
    My97Datepicker 去掉 “不合法格式或超期范围”自动纠错限制
    获取前后n天的时间
    基于spring aop的操作日志功能
    为TIF、JPG图片添加地理坐标/平面直角坐标
    NGINX 中常规优化
  • 原文地址:https://www.cnblogs.com/ssgeek/p/13723385.html
Copyright © 2020-2023  润新知