验证器-Django REST框架
验证器
验证器对于在不同类型的字段之间重用验证逻辑非常有用。
— Django文档
大多数时候,在REST框架中处理验证时,您只需依赖默认的字段验证,或者在序列化程序或字段类上编写显式验证方法。
但是,有时您可能希望将验证逻辑放入可重用组件中,以便在整个代码库中很容易地重用它。这可以通过使用验证器函数和验证器类来实现。
REST框架中的验证
Django REST框架序列化器中的验证处理方式与Django的验证方式略有不同ModelForm
班级,等级。
带着ModelForm
验证部分在表单上执行,部分在模型实例上执行。使用REST框架,验证完全在序列化程序类上执行。这是有利的,原因如下:
- 它引入了适当的关注点分离,使您的代码行为更加明显。
- 在使用快捷方式之间很容易切换。
ModelSerializer
类和使用显式Serializer
上课。使用的任何验证行为ModelSerializer
很容易复制。 - 打印
repr
序列化器实例将向您详细显示它应用的验证规则。在模型实例上没有额外的隐藏验证行为被调用。
当你使用ModelSerializer
所有这些都是自动为您处理的。如果你想下降到使用Serializer
类,则需要显式定义验证规则。
例
作为REST框架如何使用显式验证的示例,我们将使用一个简单的模型类,该类具有一个具有唯一性约束的字段。
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
如果我们打开Django shellmanage.py 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框架包含了一些在核心Django中不可用的验证器类。下面将详细介绍这些类。
UniqueValidator
此验证器可用于强制执行unique=True
模型字段的约束。它接受一个所需的参数,以及一个可选的参数。messages
论点:
-
queryset
所需-这是执行唯一性的查询集。 -
message
-验证失败时应使用的错误消息。 -
lookup
-查找用于查找值正在验证的现有实例。默认为'exact'
.
此验证器应应用于串行化字段,就像这样:
from rest_framework.validators import UniqueValidator
slug = SlugField(
max_length=100,
validators=[UniqueValidator(queryset=BlogPost.objects.all())]
)
UniqueTogetherValidator
此验证器可用于强制执行unique_together
模型实例的约束。它有两个必需的参数和一个可选参数。messages
论点:
-
queryset
所需-这是执行唯一性的查询集。 -
fields
所需-一组或多个字段名,应构成一套独特的名称。这些必须作为序列化程序类上的字段存在。 -
message
-验证失败时应使用的错误消息。
验证器应应用于串行化类,就像这样:
from rest_framework.validators import UniqueTogetherValidator
class 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']
)
]
注*UniqueTogetherValidator
类总是施加一个隐式约束,即它所应用的所有字段总是按需要处理。字段default
值是这种情况的一个例外,因为它们总是提供一个值,即使从用户输入中忽略了它。
UniqueForDateValidator
UniqueForMonthValidator
UniqueForyValidator
这些验证器可用于执行unique_for_date
, unique_for_month
和unique_for_year
模型实例的约束。他们采取以下论点:
-
queryset
所需-这是执行唯一性的查询集。 -
field
所需-将验证给定日期范围中的唯一性的字段名。这必须作为序列化程序类上的字段存在。 -
date_field
所需-字段名,用于确定唯一性约束的日期范围。这必须作为序列化程序类上的字段存在。 -
message
-验证失败时应使用的错误消息。
验证器应应用于串行化类,就像这样:
from rest_framework.validators import UniqueForYearValidator
class 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'
)
]
用于验证的日期字段始终需要出现在序列化程序类中。不能简单地依赖于模型类default=...
,因为用于默认值的值在验证运行之后才会生成。
您可能希望为此使用几种样式,这取决于您希望API的行为方式。如果你用ModelSerializer
您可能只需依赖REST框架为您生成的缺省值,但是如果您正在使用Serializer
或者只是想要更显式的控制,使用下面演示的样式。
与可写日期字段一起使用。
如果您希望日期字段是可写的,唯一值得注意的是,您应该确保它始终在输入数据中可用,或者通过设置default
参数,或通过设置required=True
.
published = serializers.DateTimeField(required=True)
与只读日期字段一起使用。
如果希望日期字段可见,但用户不能编辑,则设置read_only=True
并另外设置一个default=...
争论。
published = serializers.DateTimeField(read_only=True, default=timezone.now)
与隐藏日期字段一起使用。
如果希望对用户完全隐藏日期字段,请使用HiddenField
...此字段类型不接受用户输入,而是始终将其默认值返回给validated_data
在串行化器里。
published = serializers.HiddenField(default=timezone.now)
注*UniqueFor<Range>Validator
类施加了一个隐式约束,即应用它们的字段总是按照需要处理。字段default
值是这种情况的一个例外,因为它们总是提供一个值,即使从用户输入中忽略了它。
高级字段默认值
在序列化程序中跨多个字段应用的验证程序有时需要一个不应由api客户端提供的字段输入,但需要是可作为验证器的输入。
您可能希望用于此类验证的两种模式包括:
- 使用
HiddenField
...此字段将出现在validated_data
但不会在序列化程序输出表示中使用。 - 使用标准字段
read_only=True
,但这也包括default=…
争论。这个领域将要在序列化程序输出表示中使用,但不能由用户直接设置。
REST框架包含了一些在这种情况下可能有用的缺省值。
CurrentUserDefault
可用于表示当前用户的默认类。为了使用这一点,在实例化序列化程序时,“请求”必须作为上下文字典的一部分提供。
owner = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
CreateOnlyDefault
可用于仅在创建操作期间设置默认参数...在更新期间,将省略该字段。
它使用一个参数,这是在创建操作期间应该使用的默认值或可调用的参数。
created_at = serializers.DateTimeField(
default=serializers.CreateOnlyDefault(timezone.now)
)
验证器的限制
在某些不明确的情况下,您需要显式地处理验证,而不是依赖于默认的序列化类ModelSerializer
生成。
在这些情况下,您可能希望通过为序列化程序指定一个空列表来禁用自动生成的验证器。Meta.validators
属性。
可选字段
默认情况下,“唯一在一起”验证强制所有字段为required=True
...在某些情况下,您可能希望显式应用required=False
对于其中一个字段,在这种情况下,所需的验证行为是不明确的。
在这种情况下,通常需要将验证器从序列化程序类中排除出来,而是显式地写入任何验证逻辑,无论是在.validate()
方法,或者视图中的其他方法。
例如:
class BillingRecordSerializer(serializers.ModelSerializer):
def validate(self, attrs):
# 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.')
字段级验证
可以通过添加.validate_<field_name>
方法Serializer
子类。这在序列化文档
类基
若要编写基于类的验证器,请使用__call__
方法。基于类的验证器非常有用,因为它们允许您参数化和重用行为。
class MultipleOf(object):
def __init__(self, base):
self.base = base
def __call__(self, value):
if value % self.base != 0:
message = 'This field must be a multiple of %d.' % self.base
raise 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
网友评论