官方原文連接
本系列文章 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
複製代碼