Django REST framework API 指南(12):验证器
官方原文链接
本系列文章 github 地址
转载请注明出处
验证器
大多数情况下,您在 REST framework 中处理验证时,只需依赖默认的字段验证,或者在序列化类或字段类上编写明确的验证方法。
但是,有时你会希望将验证逻辑放置到可重用组件中,以便在整个代码库中轻松地重用它。这可以通过使用验证器函数和验证器类来实现。
REST framework 中的验证
Django REST framework 序列化器中的验证与 Django ModelForm
类中验证的工作方式有点不同。
使用 ModelForm
,验证一部分在表单上执行,一部分在模型实例上执行。使用 REST framework ,验证完全在序列化类上执行。这是有优势的,原因如下:
- 它使问题适当的分离,让代码行为变的更加清晰。
- 使用快捷的
ModelSerializer
类和使用显式的Serializer
类可以轻松切换。任何用于ModelSerializer
的验证行为都很容易复制。 - 打印序列化类实例的
repr
将会显示它应用的验证规则。在模型实例上没有额外的隐藏验证行为(因为全在序列化类上)。
当你使用 ModelSerializer
时,所有验证都会自动为你处理。如果你想要改为使用 Serializer
类,那么你需要明确定义验证规则。
举个栗子
作为 REST framework 如何使用显式验证的示例,我们将采用一个简单的模型类,该类具有唯一性约束的字段。
class CustomerReportRecord(models.Model):time_raised = models.DateTimeField(default=timezone.now, editable=False)reference = models.CharField(unique=True, max_length=20)description = models.TextField()
复制代码
下面是一个基本的 ModelSerializer
,我们可以使用它来创建或更新 CustomerReportRecord
的实例:
class CustomerReportSerializer(serializers.ModelSerializer):class Meta:model = CustomerReportRecord
复制代码
现在使用 manage.py shell
打开 Django shell
>>> from project.example.serializers import CustomerReportSerializer
>>> serializer = CustomerReportSerializer()
>>> print(repr(serializer))
CustomerReportSerializer():id = IntegerField(label='ID', read_only=True)time_raised = DateTimeField(read_only=True)reference = CharField(max_length=20, validators=[<UniqueValidator(queryset=CustomerReportRecord.objects.all())>])description = CharField(style={'type': 'textarea'})
复制代码
这里有趣的是 reference
字段。我们可以看到唯一性约束由序列化字段上的验证器明确执行。
由于这种更明确的风格,REST framework 包含一些在核心 Django 中没有的验证器类。这些类在下面详细说明。
UniqueValidator
该验证器可用于在模型字段上强制实施 unique=True
约束。它需要一个必需的参数和一个可选的 messages
参数:
queryset
必须 - 这是验证唯一性的查询集。message
- 验证失败时使用的错误消息。lookup
- 用于查找已验证值的现有实例。默认为'exact'
。
这个验证器应该应用于序列化字段,如下所示:
from rest_framework.validators import UniqueValidatorslug = SlugField(max_length=100,validators=[UniqueValidator(queryset=BlogPost.objects.all())]
)
复制代码
UniqueTogetherValidator
此验证器可用于在模型实例上强制实施 unique_together
约束。它有两个必需的参数和一个可选的 messages
参数:
queryset
必须 - 这是验证唯一性的查询集。fields
必须 - 一个存放字段名称的列表或者元组,这个集合必须是唯一的(意思是集合中的字段代表的一组值不能同时出现在两条数据中)。这些字段必须都是序列化类中的字段。message
- 验证失败时使用的错误消息。
验证器应该应用于序列化类,如下所示:
from rest_framework.validators import UniqueTogetherValidatorclass ExampleSerializer(serializers.Serializer):# ...class Meta:# ToDo items belong to a parent list, and have an ordering defined# by the 'position' field. No two items in a given list may share# the same position.validators = [UniqueTogetherValidator(queryset=ToDoItem.objects.all(),fields=('list', 'position'))]
复制代码
注意: UniqueTogetherValidation
类总是施加一个隐式约束,即它所应用的所有字段都是按需处理的。具有 default
值的字段是一个例外,因为它们总是提供一个值,即使在用户输入中省略了这个值。
UniqueForDateValidator
UniqueForMonthValidator
UniqueForYearValidator
这些验证器可用于强制实施模型实例上的 unique_for_date
,unique_for_month
和 unique_for_year
约束。他们有以下参数:
queryset
必须 - 这是验证唯一性的查询集。field
必须 - 在给定日期范围内需要被验证唯一性的字段的名称。该字段必须是序列化类中的字段。date_field
必须 - 将用于确定唯一性约束的日期范围的字段名称。该字段必须是序列化类中的字段。message
- 验证失败时使用的错误消息。
验证器应该应用于序列化类,如下所示:
from rest_framework.validators import UniqueForYearValidatorclass ExampleSerializer(serializers.Serializer):# ...class Meta:# Blog posts should have a slug that is unique for the current year.validators = [UniqueForYearValidator(queryset=BlogPostItem.objects.all(),field='slug',date_field='published')]
复制代码
我解释下,上面例子的意思是,在
published
日期所在的年份中,slug
字段的值必须唯一,注意,不是要和published
完全相等的日期,而是年份相等。unique_for_date
,unique_for_month
同理。
用于验证的日期字段应该始终存在于序列化类中。你不能简单地依赖模型类 default=...
,因为默认值在验证运行之后才会生成。
你可能需要使用几种样式,具体取决于你希望 API 如何展现。如果你使用的是 ModelSerializer
,可能只需依赖 REST framework 为你生成的默认值,但如果你使用的是 Serializer
或需要更明确的控制,请使用下面演示的样式。
与可写日期字段一起使用。
如果你希望日期字段是可写的,唯一值得注意的是你应该确保它始终可用于输入数据中,可以通过设置 default
参数或通过设置 required=True
来实现。
published = serializers.DateTimeField(required=True)
复制代码
与只读日期字段一起使用。
如果你希望日期字段可见,但用户无法编辑,请设置 read_only=True
并另外设置 default=...
参数。
published = serializers.DateTimeField(read_only=True, default=timezone.now)
复制代码
该字段对用户不可写,但默认值仍将传递给 validated_data
。
与隐藏的日期字段一起使用。
如果你希望日期字段对用户完全隐藏,请使用 HiddenField
。该字段类型不接受用户输入,而是始终将其默认值返回到序列化类中的 validated_data
。
published = serializers.HiddenField(default=timezone.now)
复制代码
注意: UniqueFor<Range>Validation
类总是施加一个隐式约束,即它所应用的所有字段都是按需处理的。具有 default
值的字段是一个例外,因为它们总是提供一个值,即使在用户输入中省略了这个值。
Advanced field defaults
在序列化类的多个字段中应用的验证器有时不需要由 API 客户端提供的字段输入,但它可以用作验证器的输入。
有两种模式可能需要这种验证:
- 使用
HiddenField
。该字段将出现在validated_data
中,但不会用在序列化输出表示中。 - 使用
read_only=True
的标准字段,同时也包含default=...
参数。该字段将用于序列化输出表示中,但不能由用户直接设置。
REST framework 包含一些在这种情况下可能有用的默认值。
CurrentUserDefault
可用于表示当前用户的默认类。为了使用它,在实例化序列化类时,'request'
必须作为上下文字典的一部分提供。
owner = serializers.HiddenField(default=serializers.CurrentUserDefault()
)
复制代码
CreateOnlyDefault
可用于在 create 操作期间仅设置默认参数的默认类。在 update 期间,该字段被省略。
它接受一个参数,这是在 create 操作期间应该使用的默认值或可调用参数。
created_at = serializers.DateTimeField(read_only=True,default=serializers.CreateOnlyDefault(timezone.now)
)
复制代码
验证器的限制
有一些不明确的情况,你需要显示处理验证,而不是依赖 ModelSerializer
生成的默认序列化类。
在这些情况下,你可能希望通过为序列化类 Meta.validators
属性指定一个空列表来禁用自动生成的验证器。
可选字段
默认情况下 "unique together" 验证强制所有字段都是 required=True
。在某些情况下,你可能希望显式将 required=False
应用于其中一个字段,在这种情况下,验证所需的行为是不明确的。
在这种情况下,你通常需要从序列化类中排除验证器,并且在 .validate()
方法中或在视图中显式地编写验证逻辑。
例如:
class BillingRecordSerializer(serializers.ModelSerializer):def validate(self, data):# Apply custom validation either here, or in the view.class Meta:fields = ('client', 'date', 'amount')extra_kwargs = {'client': {'required': 'False'}}validators = [] # Remove a default "unique together" constraint.
复制代码
更新嵌套序列化类
将更新应用于现有实例时,唯一性验证器将从唯一性检查中排除当前实例。当前实例在唯一性检查的上下文中可用,因为它作为序列化程序中的一个属性存在,最初在实例化序列化类时已使用 instance=...
传递。
在嵌套序列化类上进行更新操作时,无法应用此排除,因为该实例不可用。
你可能又一次需要明确地从序列化类中移除验证器,并将验证约束的代码显式写入 .validate()
方法或视图中。
调试复杂的案例
如果你不确定 ModelSerializer
类的默认行为,那么运行 manage.py shell
并打印序列化类实例通常是一个好主意,以便你可以检查它自动生成的字段和验证器。
>>> serializer = MyComplexModelSerializer()
>>> print(serializer)
class MyComplexModelSerializer:my_fields = ...
复制代码
还要记住,在复杂情况下,明确定义序列化类通常会更好,而不是依赖默认的 ModelSerializer
行为。虽然这样会写更多的代码,但确保了最终的行为更加透明。
编写自定义验证器
你可以使用 Django 现有的验证器,也可以编写自定义验证器。
基于函数
验证器可以是任何可调用对象,在失败时引发 serializers.ValidationError
。
def even_number(value):if value % 2 != 0:raise serializers.ValidationError('This field must be an even number.')
复制代码
字段级验证
你可以通过向 Serializer
子类添加 .validate_<field_name>
方法来指定自定义字段级验证。
基于类
要编写一个基于类的验证器,请使用 __call__
方法。基于类的验证器很有用,因为它们允许参数化和重用行为。
class MultipleOf(object):def __init__(self, base):self.base = basedef __call__(self, value):if value % self.base != 0:message = 'This field must be a multiple of %d.' % self.baseraise serializers.ValidationError(message)
复制代码
使用 set_context()
在一些高级的情况下,你可能想要在验证器中获取正在被验证的序列化字段。这时,你可以通过在基于类的验证器上声明 set_context
方法来完成此操作。
def set_context(self, serializer_field):# Determine if this is an update or a create operation.# In `__call__` we can then use that information to modify the validation behavior.self.is_update = serializer_field.parent.instance is not None
复制代码
Django REST framework API 指南(12):验证器相关推荐
- Django REST framework API 指南(2):响应
Django REST framework API 指南(1):请求 Django REST framework API 指南(2):响应 Django REST framework API 指南(3 ...
- Django REST framework API 指南(11):序列化·关系
官方原文链接 本系列文章 github 地址 转载请注明出处 Serializer 关系 关系字段用于表示模型关系. 它们可以应用于 ForeignKey,ManyToManyField 和 OneT ...
- Django REST framework API 指南(25):状态码
官方原文链接 本系列文章 github 地址 转载请注明出处 状态码 不建议在你的响应中使用裸露(直接使用数字)的状态码. REST framework 包含一组命名常量,你可以使用它们使代码更加清晰 ...
- Django REST framework API开发
REST 介绍 RESTful API 设计 实现API的两种方式 FBV 视图函数 urlpatterns = [url(r'^user/$', views.user),url(r'^user/ad ...
- Django REST Framework API Guide 01
之前按照REST Framework官方文档提供的简介写了一系列的简单的介绍博客,说白了就是翻译了一下简介,而且翻译的很烂.到真正的生产时,就会发现很鸡肋,连熟悉大概知道rest framework都 ...
- Django REST Framework API Guide 02
本节大纲 1.Generic Views 2.ViewSets 1.Generic Views CBV的主要的一个优点就是极大的允许了对于代码的从用.自然,rest framework取其优势,提供 ...
- Django REST Framework API Guide 07
本节大纲 1.Permissions 2.Throttling Permissions 权限是用来授权或者拒绝用户访问API的不同部分的不同的类的.基础的权限划分 1.IsAuthenticated ...
- JWT验证机制【刘新宇】【Django REST framework中使用JWT】
JWT 在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证.我们不再使用Session认证机制,而使用Json Web Token认证机制. 什么是JWT Json web t ...
- 谈谈Django REST Framework(DRF)中的序列化器
摘要 Django REST Framework(DRF)是一个强大的工具,可以帮助我们构建和处理RESTful API.其中的序列化器(Serializers)是其核心组件之一,它允许我们快速有效地 ...
最新文章
- Linux Shell 常用快捷键
- 关于「Xception」和「DeepLab V3+」的那些事
- 微计算机和微处理器的区别,CPU和微处理器的区别
- 1288:三角形最佳路径问题-2019-07-04
- 【Java从0到架构师】Zookeeper - 安装、核心工作机制、基本命令
- 单链表的读取、插入与删除
- hibernate oracle 读写分离_ASP.NET CORE 国产最火前后端完全分离框架BCVP
- Python学习—2048小游戏等4个小练习
- 第六章 Realm及相关对象——《跟我学Shiro》[张开涛]
- macOS Catalina 10.15.7 ISO/CDR 虚拟机镜像下载
- allegro 自定义快捷键
- oracle中文转全拼音,汉字转拼音的Oracle函数
- css定位(二)---css中粘性定位(sticky)---C3新增属性
- php post伪装ip,PHP中用CURL伪造IP来源的方法
- mac下如何解压7z
- 很火的区块链头部游戏《Axie Infinity》单日盈收竟与王者荣耀争锋,它是什么?
- java基础复习——day18(Stream流 ; 反射)
- Steve Hui:云联云是中国云计算的最好切入点(4月刊推荐)
- Lettuce: Connection to xxx not allowed. This partition is not known in the cluster view.
- Unity 编辑器开发实战【Custom Editor】- 为UI视图制作动画编辑器
热门文章
- 李彦宏:用“工程思维”做自动驾驶
- 吴军:既能得诺贝尔奖,又能生产高科技产品,美国的科研机制是如何运行的?...
- 自动驾驶出行,进入下半场
- 希尔伯特著名的第六问题 – 原来麦克斯韦早就有解?
- 5G新标准将延迟3个月发布,但5G“新战场”已经明确
- CES 2019开胃菜竟然是芯片,英特尔 英伟达 高通 华为 AMD已经开打!
- 边缘计算芯片格局分析
- 包揽全球50%以上份额,中美发力超级计算
- “芯痛”之下阿里苦心研发NPU AI芯片究竟哪款PU更厉害?
- 新技术:如何用VR训练机器人?