目录
- 1、准备工作
- 2、字段验证
- 3、validate_字段名验证
- 4、validate验证
- 5、validators验证器验证
- 6、小结
- 附:常用字段和参数
使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。
在获取反序列化的客户端数据前,必须在视图中调用序列化对象的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页面时,显示的字段帮助提示信息 |