本文首發於公眾號:Hunter後端 原文鏈接:Django筆記四十二之model使用validator驗證器 這一篇筆記介紹一下 model 里的 validator 驗證器。 首先,這是個什麼東西呢? 在 model 的第四篇筆記里,我們介紹了欄位的一些屬性,比如是否允許為空,varchar 類型 ...
本文首發於公眾號:Hunter後端
這一篇筆記介紹一下 model 里的 validator 驗證器。
首先,這是個什麼東西呢?
在 model 的第四篇筆記里,我們介紹了欄位的一些屬性,比如是否允許為空,varchar 類型的欄位的最大長度等。
一般在存儲前,我們要手動對數據進行一些校驗,比如判斷前端傳入的欄位是否為空,傳入的字元最大長度是否超過我們規定的長度等。
而 validator 驗證器就給我們提供了一個簡便的方式可以在存儲數據前自動進行校驗。
以下是本篇筆記目錄:
- 自定義驗證器
- 引用驗證器
- 校驗函數
- 測試校驗函數
- 系統驗證器介紹
1、自定義驗證器
我們下麵會在 model 中調用一個驗證器,它的作用是只允許保存偶數,如果是奇數則會引發 ValidationError。
示例如下:
from django.core.exceptions import ValidationError
def validate_even(value):
if value % 2 != 0:
raise ValidationError(f"{value} is not an even number")
如上所示,一個簡單的驗證器就完成了,它是一個函數,我們可以手動調用,傳入奇數或者偶數嘗試,傳入偶數不會發生什麼,但是傳入奇數則會引發一個 ValidationError 的報錯。
2、引用驗證器
定義好一個驗證器之後,我們在 model 中引入。
我們這裡新建一個 model,名為 TestValidate:
class TestValidate(models.Model):
even_field = models.IntegerField(default=0, validators=[validate_even])
如上,一個驗證器的引入就完成了,當我們在嘗試對數據進行保存的時候,比如創建一條數據:
TestValidate.objects.create(even_field=1)
傳入的數據值為 1,是不合法的,但是卻可以保存,為什麼呢?
這裡介紹一下觸發驗證器的機制。
雖然我們在 model 里引入了驗證器,但是 Django 系統並不會自動觸發,而需要我們進行手動去觸發校驗操作。
我們在下一節介紹一下進行校驗的四個函數:
- clean_fields()
- clean()
- validate_unique()
- full_clean()
3、校驗函數
前面介紹到需要在保存前手動調用校驗函數,先介紹一下四個校驗函數
1.clean_fields(exclude=None)
接收 exclude 參數,參數類型為列表,列表元素為欄位名稱,表示傳入的欄位不進行校驗,如果不傳該參數,則預設對 model 的所有欄位進行校驗。
這個函數的操作是,校驗 model 里中所有不合法的欄位數據,比如沒有設置允許為空,但欄位的值為空,以及包括設置了 validators 參數的欄位。
比如前面我們設置了 even_field
欄位只允許保存偶數,那麼在 save() 操作前,調用 clean_fields() 函數,則會引發 validate_even 的報錯。
2.clean()
預設是一個空函數,用於我們繼承重寫一些校驗操作,我們可以自定義一些 model 的限制需求,比如,它可以進行不同欄位間的關聯校驗
3.validate_unique(exclude=None)
驗證的是欄位數據是否違反唯一性約束,其實也就是獲取 model 里所有包含唯一性約束的欄位,然後去資料庫里查詢是否包含同樣的數據,如果存在,則引發驗證報錯。
唯一性約束包含 unique,unique_for_date,unique_for_year,unique_for_month 等,接收 exclude 參數,和 clean_fields() 函數一致,exclude 為不進行校驗的欄位
4.full_clean(exclude=None, validate_unique=True)
所以我們在進行 model 的 save() 操作前,可以根據我們的需要進行相應的校驗操作
也可以直接調用 full_clean() 函數,這個函數會依次調用 clean_fields,clean,和 validate_unique 函數。
full_clean() 接收兩個參數,一個 exclude,接收不校驗的欄位列表,一個validate_unique,為布爾型數據,表示是否需要進行唯一性校驗
4、測試校驗函數
下麵我們挨個對校驗函數進行處理測試操作。
首先重新設置一下 model:
# blog/models.py
from django.core.exceptions import ValidationError
from django.db import models
def validate_even(value):
if value % 2 != 0:
raise ValidationError(f"{value} is not an even number")
class TestValidate(models.Model):
even_field = models.IntegerField(default=0, validators=[validate_even])
name = models.CharField(unique=True, max_length=20, default=None)
1. 測試clean_fields
測試這個函數,我們只需要在給實例化後的 model 調用這個函數即可:
test_1 = TestValidate.objects.create(even_field=2, name="abc")
test_2 = TestValidate()
test_2.even_field = 1
test_2.name = "abc"
test_2.clean_fields()
在 test_2 調用 clean_fields() 後,系統會校驗 TestValidate 實例化後所有欄位的的不合法數據,以及額外的校驗操作,即 validators 中定義的校驗邏輯
比如在這裡 even_field = 1 會被驗證不通過,返回一個報錯
clean_fields() 函數可以接收 exclude 參數,可用於傳入不進行校驗的欄位名稱列表,比如這裡我們設置:
test_2.clean_fields(exclude=["even_field"])
那麼即便 even_field 的欄位值不合法,也會順利保存。
2. 測試validate_unique
在這裡我們設置了 name 欄位為 unique 唯一性約束,也就是說在 TestValidate 表裡 name 欄位不能存在相同的值
前面我們創建了一條 name = "abc"
的值,接著我們設置 test_2.name = "abc"
,然後執行:
test_2.name = "abc"
test_2.validate_unique()
系統會獲取 TestValidate 里所有設置了 unique 的欄位,然後獲取欄位對應的值去資料庫進行唯一性校驗,在我們上面的例子里設置 name="abc" 是不通過的。
3. 測試clean
系統提供了 clean() 函數,可用於我們創建自定義的驗證操作
比如,我們設置當 even_field = 4 且 name="張三" 的時候,這條數據就是不合法的,我們可以如此先設置 clean() 函數:
class TestValidate(models.Model):
even_field = models.IntegerField(default=0, validators=[validate_even])
name = models.CharField(unique=True, max_length=20, default=None)
def clean(self):
if self.even_field == 4 and self.name == "張三":
raise ValidationError("指定 even_field 和 name 不合法")
再執行:
from blog.models import TestValidate
test_3 = TestValidate()
test_3.even_field = 4
test_3.name = "張三"
test_3.clean()
4. 測試full_clean
如果我們想在 save() 前都調用一遍上面介紹的校驗函數,可以直接執行 full_clean(),它會按照順序挨個調用 clean_fields、clean、validate_unique
full_clean() 接收兩個參數,一個 exclude,接收不校驗的欄位列表,一個validate_unique,為 布爾型數據,確定是否需要進行唯一性校驗
5. 重寫save()
前面我們會在每次進行 save() 前都手動執行校驗函數,我們可以重寫 save(),這樣在每次創建和保存一個 model 實例的時候,就不需要手動調用我們前面的校驗函數了,操作示例如下:
class TestValidate(models.Model):
even_field = models.IntegerField(default=0, validators=[validate_even])
name = models.CharField(unique=True, max_length=20, default=None)
def save(self, *args, **kwargs):
self.clean_fields()
self.clean()
self.validate_unique()
super(TestValidate, self).save(*args, **kwargs)
而 full_clean() 可以直接調用這三個校驗函數,所以上面等效於:
def save(self, *args, **kwargs):
self.full_clean()
super(TestValidate, self).save(*args, **kwargs)
清空表數據後,我們可以進行下麵的測試:
from blog.models import TestValidate
test_1 = TestValidate.objects.create(even_field=2, name="abc")
test_2 = TestValidate.objects.create(even_field=2, name="abc") # 會報校驗的錯誤
test_3 = TestValidate()
test_3.even_field=3
test_3.name = "def"
test_3.save() # 報校驗的錯誤
6. 繼承BaseModel
我們上面的操作是基於單個 model 的 save() 操作,如果我們要對每個 model 都實現這種自動進行校驗的操作,那麼則需要對每個 model 都進行這種 save() 的繼承重寫操作
如果想要實現每個 model 自動實現這種校驗的操作,我們可以編寫一個 BaseModel,在 BaseModel 里重寫 save() 操作,然後每個 model 都繼承 BaseModel,就可以實現我們的目的了。
class BaseModel(models.Model):
class Meta:
abstract = True
def save(self, *args, **kwargs):
self.full_clean()
super(BaseModel, self).save(*args, **kwargs)
class TestValidate(BaseModel):
even_field = models.IntegerField(default=0, validators=[validate_even])
name = models.CharField(unique=True, max_length=20, default=None)
這種操作也可以用於添加基礎欄位,比如我們想為每個 model 都添加 updated_time 和 created_time 作為基礎欄位,用於記錄數據寫入和更新時間,可以在 BaseModel 里添加:
class BaseModel(models.Model):
updated_time = models.DateTimeField(auto_now=True)
created_time = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
def save(self, *args, **kwargs):
self.full_clean()
super(BaseModel, self).save(*args, **kwargs)
5、系統驗證器介紹
前面介紹的是自定義的驗證器,以及如何使用,其實Django系統里為我們實現了很多基礎的驗證器:
- EmailValidator 驗證郵箱格式
- MaxValueValidator 驗證最大值
- MinValueValidator 驗證最小值
- MaxLengthValidator 驗證最大長度
- MinLengthValidator 驗證最小長度
- RegexValidator 驗證正則表達式
我們設置的 model 如下:
class TestValidate(BaseModel):
even_field = models.IntegerField(default=0, validators=[validate_even])
name = models.CharField(unique=True, max_length=20, default=None)
email = models.CharField(max_length=100, default="", validators=[EmailValidator(message="email不合法")])
count = models.IntegerField(default=8, validators=[MaxValueValidator(limit_value=20), MinValueValidator(limit_value=5)])
char_str = models.CharField(max_length=100, validators=[MaxLengthValidator(limit_value=90), MinLengthValidator(limit_value=20)], default="")
telephone = models.CharField(max_length=11, validators=[RegexValidator("1[345678]\d{9}")], default="")
用上了上面這幾個自帶的驗證器,接下來我們對 email,count,char_str,telephone 幾個欄位設置不合法的值進行創建:
TestValidate.objects.create(
even_field=2,
name="abc",
email="12314234",
count=25,
char_str="abcd",
telephone="122282883"
)
然後會發現從 email 到 telephone 欄位都報了欄位值不合法的錯,我們將其挨個修正為 model 里符合校驗規則的數據即可正常創建數據了。
如果想獲取更多相關文章,可掃碼關註閱讀: