官方原文连接
本系列文章 github 地址
转载请注明出处python
大多数状况下,您在 REST framework 中处理验证时,只需依赖默认的字段验证,或者在序列化类或字段类上编写明确的验证方法。git
可是,有时你会但愿将验证逻辑放置到可重用组件中,以便在整个代码库中轻松地重用它。这能够经过使用验证器函数和验证器类来实现。github
Django REST framework 序列化器中的验证与 Django ModelForm
类中验证的工做方式有点不一样。shell
使用 ModelForm
,验证一部分在表单上执行,一部分在模型实例上执行。使用 REST framework ,验证彻底在序列化类上执行。这是有优点的,缘由以下:django
ModelSerializer
类和使用显式的 Serializer
类能够轻松切换。任何用于 ModelSerializer
的验证行为都很容易复制。repr
将会显示它应用的验证规则。在模型实例上没有额外的隐藏验证行为(由于全在序列化类上)。当你使用 ModelSerializer
时,全部验证都会自动为你处理。若是你想要改成使用 Serializer
类,那么你须要明肯定义验证规则。api
做为 REST framework 如何使用显式验证的示例,咱们将采用一个简单的模型类,该类具备惟一性约束的字段。ide
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 shellpost
>>> 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
字段。咱们能够看到惟一性约束由序列化字段上的验证器明确执行。ui
因为这种更明确的风格,REST framework 包含一些在核心 Django 中没有的验证器类。这些类在下面详细说明。
该验证器可用于在模型字段上强制实施 unique=True
约束。它须要一个必需的参数和一个可选的 messages
参数:
queryset
必须 - 这是验证惟一性的查询集。message
- 验证失败时使用的错误消息。lookup
- 用于查找已验证值的现有实例。默认为 'exact'
。这个验证器应该应用于序列化字段,以下所示:
from rest_framework.validators import UniqueValidator
slug = SlugField(
max_length=100,
validators=[UniqueValidator(queryset=BlogPost.objects.all())]
)
复制代码
此验证器可用于在模型实例上强制实施 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')
)
]
复制代码
注意: UniqueTogetherValidation
类老是施加一个隐式约束,即它所应用的全部字段都是按需处理的。具备 default
值的字段是一个例外,由于它们老是提供一个值,即便在用户输入中省略了这个值。
这些验证器可用于强制实施模型实例上的 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'
)
]
复制代码
我解释下,上面例子的意思是,在
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
值的字段是一个例外,由于它们老是提供一个值,即便在用户输入中省略了这个值。
在序列化类的多个字段中应用的验证器有时不须要由 API 客户端提供的字段输入,但它能够用做验证器的输入。
有两种模式可能须要这种验证:
HiddenField
。该字段将出如今 validated_data
中,但不会用在序列化输出表示中。read_only=True
的标准字段,同时也包含 default=...
参数。该字段将用于序列化输出表示中,但不能由用户直接设置。REST framework 包含一些在这种状况下可能有用的默认值。
可用于表示当前用户的默认类。为了使用它,在实例化序列化类时,'request'
必须做为上下文字典的一部分提供。
owner = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
复制代码
可用于在 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 = 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
复制代码