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

微信扫一扫,分享到朋友圈

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

附:常用字段和参数

常用字段类型:

字段 字段构造方式
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(max length=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页面时,显示的字段帮助提示信息

Python仅用3行代码就能输出花式字符串图集,同事直呼666!

上一篇

你也可能喜欢

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

长按储存图像,分享给朋友