驗證器 (Validators)


驗證器對於在不同類型的字段之間重用驗證邏輯非常有用。—— Django 文檔

大多數情況下,您在 REST framework 中處理驗證時,只需依賴默認的字段驗證,或者在序列化器或字段類上編寫顯式的驗證方法。

但是,有時您需要將驗證邏輯放入可重用的組件中,以便可以在整個代碼庫中輕松地重用它。這可以通過使用驗證器函數和驗證器類來實現。

REST framework 中的驗證 (Validation in 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 - 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 項屬於父列表,並具有由 'position' 字段定義的排序。給定列表中沒有兩個項目可以共享相同的位置。 validators = [ UniqueTogetherValidator( queryset=ToDoItem.objects.all(), fields=('list', 'position') ) ] 

注意UniqueTogetherValidation 類始終施加一個隱式約束,即它所應用的所有字段都是按必須處理的。具有 default 值的字段是個例外,因為它們總是提供一個值,即使在用戶輸入中省略。


UniqueForDateValidator

UniqueForMonthValidator

UniqueForYearValidator

這些驗證器可用於在模型實例上強制執行 unique_for_dateunique_for_month 和 unique_for_year 約束。他們有以下參數:

  • queryset 必須 - 這是強制執行唯一性的查詢集。
  • fields 必須 - 將驗證給定日期范圍中唯一性的字段名。這必須作為序列化類中的字段存在。
  • 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 framework 為您生成的默認值,但如果你使用的是 Serializer 或只想更明確的控制,請使用下面演示的樣式。

使用可寫日期字段 (Using with a writable date field.)

如果您希望日期字段可寫,唯一值得注意的是您應確保輸入數據始終可用,或者通過設置 default 參數,或者設置 required = True

published = serializers.DateTimeField(required=True) 

使用只讀日期字段 (Using with a read-only date field.)

如果你希望日期字段可見,但用戶無法編輯,請設置 read_only=True 並另外設置 default=... 參數。

published = serializers.DateTimeField(read_only=True, default=timezone.now) 

該字段對用戶不可寫,但默認值仍將傳遞給 validated_data

使用隱藏日期字段 (Using with a hidden date field.)

如果您希望日期字段對用戶完全隱藏,請使用 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

可用於在創建操作期間僅設置默認參數的默認類。在更新期間,該字段被省略。

它接受一個參數,這是在創建操作期間應該使用的默認值或可調用參數。

created_at = serializers.DateTimeField( default=serializers.CreateOnlyDefault(timezone.now) ) 

驗證器的限制 (Limitations of validators)

有一些模棱兩可的情況,您需要顯式處理驗證,而不是依賴於 ModelSerializer 生成的默認序列化器類。

在這些情況下,您可能希望通過為序列化器 Meta.validators 屬性指定一個空列表來禁用自動生成的驗證器。

可選字段 (Optional fields)

默認情況下 "unique together" 驗證強制所有字段都是 required=True。在某些情況下,您可能希望顯式的將 required=False 應用到其中一個字段,在這種情況下,期望的驗證行為是不明確的。

在這種情況下,您通常需要從序列化器類中排除驗證器,而不是顯式地編寫任何驗證邏輯,無論是在 .validate() 方法中,還是在視圖中。

舉個栗子:

class BillingRecordSerializer(serializers.ModelSerializer): def validate(self, data): # 在這里或視圖中應用自定義驗證。 class Meta: fields = ('client', 'date', 'amount') extra_kwargs = {'client': {'required': False}} validators = [] # 刪除默認的 "unique together" 約束。 

更新嵌套的序列化器 (Updating nested serializers)

將更新應用於現有實例時,唯一性驗證器將從唯一性檢查中排除當前實例。當前實例在唯一性檢查的上下文中可用,因為它作為序列化器中的屬性存在,最初在實例化序列化器時使用 instance=... 傳遞。

在嵌套序列化器上進行更新操作時,無法應用此排除,因為該實例不可用。

同樣,您可能希望從序列化器類中顯式刪除驗證器,並在 .validate() 方法或視圖中顯式地為驗證約束編寫代碼。

調試復雜案例 (Debugging complex cases)

如果您不確定 ModelSerializer 類將生成什么行為,那么運行 manage.py shell 通常是個好主意,並打印序列化器的實例,以便您可以檢查自動生成的字段和驗證器。

>>> serializer = MyComplexModelSerializer() >>> print(serializer) class MyComplexModelSerializer: my_fields = ... 

還要記住,對於復雜的情況,通常最好顯式地定義序列化器類,而不是依賴於默認的 ModelSerializer 行為。這涉及更多代碼,但確保結果行為更透明。


編寫自定義驗證器 (Writing custom validators)

您可以使用任何 Django 現有的驗證器,也可以編寫自己的自定義驗證器。

基於函數 (Function based)

驗證器可以是任何可調用函數,在失敗時引發 serializers.ValidationError

def even_number(value): if value % 2 != 0: raise serializers.ValidationError('This field must be an even number.') 

字段級驗證 (Field-level validation)

您可以通過將 .validate_<field_name> 方法添加到 Serializer 子類來指定自定義字段級驗證。這在 Serializer 文檔中有記錄

基於類 (Class-based)

要編寫基於類的驗證器,請使用 __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() (Using set_context())

在某些高級情況下,您可能希望將驗證器傳遞給作為附加上下文使用的序列化器字段。您可以通過在基於類的驗證器上聲明 set_context 方法來實現。

def set_context(self, serializer_field): # 確定這是更新還是創建操作。 # 在 `__call__` 中,我們可以使用該信息來修改驗證行為。 self.is_update = serializer_field.parent.instance is not None


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM