本文首發(fā)于公眾號:Hunter后端
原文鏈接:Django筆記四十二之model使用validator驗(yàn)證器
這一篇筆記介紹一下 model 里的 validator 驗(yàn)證器。
首先,這是個(gè)什么東西呢?
在 model 的第四篇筆記里,我們介紹了字段的一些屬性,比如是否允許為空,varchar 類型的字段的最大長度等。
一般在存儲前,我們要手動對數(shù)據(jù)進(jìn)行一些校驗(yàn),比如判斷前端傳入的字段是否為空,傳入的字符最大長度是否超過我們規(guī)定的長度等。
而 validator 驗(yàn)證器就給我們提供了一個(gè)簡便的方式可以在存儲數(shù)據(jù)前自動進(jìn)行校驗(yàn)。
以下是本篇筆記目錄:
- 自定義驗(yàn)證器
- 引用驗(yàn)證器
- 校驗(yàn)函數(shù)
- 測試校驗(yàn)函數(shù)
- 系統(tǒng)驗(yàn)證器介紹
1、自定義驗(yàn)證器
我們下面會在 model 中調(diào)用一個(gè)驗(yàn)證器,它的作用是只允許保存偶數(shù),如果是奇數(shù)則會引發(fā) ValidationError。
示例如下:
from django.core.exceptions import ValidationError
def validate_even(value):
if value % 2 != 0:
raise ValidationError(f"{value} is not an even number")
如上所示,一個(gè)簡單的驗(yàn)證器就完成了,它是一個(gè)函數(shù),我們可以手動調(diào)用,傳入奇數(shù)或者偶數(shù)嘗試,傳入偶數(shù)不會發(fā)生什么,但是傳入奇數(shù)則會引發(fā)一個(gè) ValidationError 的報(bào)錯(cuò)。
2、引用驗(yàn)證器
定義好一個(gè)驗(yàn)證器之后,我們在 model 中引入。
我們這里新建一個(gè) model,名為 TestValidate:
class TestValidate(models.Model):
even_field = models.IntegerField(default=0, validators=[validate_even])
如上,一個(gè)驗(yàn)證器的引入就完成了,當(dāng)我們在嘗試對數(shù)據(jù)進(jìn)行保存的時(shí)候,比如創(chuàng)建一條數(shù)據(jù):
TestValidate.objects.create(even_field=1)
傳入的數(shù)據(jù)值為 1,是不合法的,但是卻可以保存,為什么呢?
這里介紹一下觸發(fā)驗(yàn)證器的機(jī)制。
雖然我們在 model 里引入了驗(yàn)證器,但是 Django 系統(tǒng)并不會自動觸發(fā),而需要我們進(jìn)行手動去觸發(fā)校驗(yàn)操作。
我們在下一節(jié)介紹一下進(jìn)行校驗(yàn)的四個(gè)函數(shù):
- clean_fields()
- clean()
- validate_unique()
- full_clean()
3、校驗(yàn)函數(shù)
前面介紹到需要在保存前手動調(diào)用校驗(yàn)函數(shù),先介紹一下四個(gè)校驗(yàn)函數(shù)
1.clean_fields(exclude=None)
接收 exclude 參數(shù),參數(shù)類型為列表,列表元素為字段名稱,表示傳入的字段不進(jìn)行校驗(yàn),如果不傳該參數(shù),則默認(rèn)對 model 的所有字段進(jìn)行校驗(yàn)。
這個(gè)函數(shù)的操作是,校驗(yàn) model 里中所有不合法的字段數(shù)據(jù),比如沒有設(shè)置允許為空,但字段的值為空,以及包括設(shè)置了 validators 參數(shù)的字段。
比如前面我們設(shè)置了 even_field
字段只允許保存偶數(shù),那么在 save() 操作前,調(diào)用 clean_fields() 函數(shù),則會引發(fā) validate_even 的報(bào)錯(cuò)。
2.clean()
默認(rèn)是一個(gè)空函數(shù),用于我們繼承重寫一些校驗(yàn)操作,我們可以自定義一些 model 的限制需求,比如,它可以進(jìn)行不同字段間的關(guān)聯(lián)校驗(yàn)
3.validate_unique(exclude=None)
驗(yàn)證的是字段數(shù)據(jù)是否違反唯一性約束,其實(shí)也就是獲取 model 里所有包含唯一性約束的字段,然后去數(shù)據(jù)庫里查詢是否包含同樣的數(shù)據(jù),如果存在,則引發(fā)驗(yàn)證報(bào)錯(cuò)。
唯一性約束包含 unique,unique_for_date,unique_for_year,unique_for_month 等,接收 exclude 參數(shù),和 clean_fields() 函數(shù)一致,exclude 為不進(jìn)行校驗(yàn)的字段
4.full_clean(exclude=None, validate_unique=True)
所以我們在進(jìn)行 model 的 save() 操作前,可以根據(jù)我們的需要進(jìn)行相應(yīng)的校驗(yàn)操作
也可以直接調(diào)用 full_clean() 函數(shù),這個(gè)函數(shù)會依次調(diào)用 clean_fields,clean,和 validate_unique 函數(shù)。
full_clean() 接收兩個(gè)參數(shù),一個(gè) exclude,接收不校驗(yàn)的字段列表,一個(gè)validate_unique,為布爾型數(shù)據(jù),表示是否需要進(jìn)行唯一性校驗(yàn)
4、測試校驗(yàn)函數(shù)
下面我們挨個(gè)對校驗(yàn)函數(shù)進(jìn)行處理測試操作。
首先重新設(shè)置一下 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
測試這個(gè)函數(shù),我們只需要在給實(shí)例化后的 model 調(diào)用這個(gè)函數(shù)即可:
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 調(diào)用 clean_fields() 后,系統(tǒng)會校驗(yàn) TestValidate 實(shí)例化后所有字段的的不合法數(shù)據(jù),以及額外的校驗(yàn)操作,即 validators 中定義的校驗(yàn)邏輯
比如在這里 even_field = 1 會被驗(yàn)證不通過,返回一個(gè)報(bào)錯(cuò)
clean_fields() 函數(shù)可以接收 exclude 參數(shù),可用于傳入不進(jìn)行校驗(yàn)的字段名稱列表,比如這里我們設(shè)置:
test_2.clean_fields(exclude=["even_field"])
那么即便 even_field 的字段值不合法,也會順利保存。
2. 測試validate_unique
在這里我們設(shè)置了 name 字段為 unique 唯一性約束,也就是說在 TestValidate 表里 name 字段不能存在相同的值
前面我們創(chuàng)建了一條 name = "abc"
的值,接著我們設(shè)置 test_2.name = "abc"
,然后執(zhí)行:
test_2.name = "abc"
test_2.validate_unique()
系統(tǒng)會獲取 TestValidate 里所有設(shè)置了 unique 的字段,然后獲取字段對應(yīng)的值去數(shù)據(jù)庫進(jìn)行唯一性校驗(yàn),在我們上面的例子里設(shè)置 name="abc" 是不通過的。
3. 測試clean
系統(tǒng)提供了 clean() 函數(shù),可用于我們創(chuàng)建自定義的驗(yàn)證操作
比如,我們設(shè)置當(dāng) even_field = 4 且 name="張三" 的時(shí)候,這條數(shù)據(jù)就是不合法的,我們可以如此先設(shè)置 clean() 函數(shù):
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 不合法")
再執(zhí)行:
from blog.models import TestValidate
test_3 = TestValidate()
test_3.even_field = 4
test_3.name = "張三"
test_3.clean()
4. 測試full_clean
如果我們想在 save() 前都調(diào)用一遍上面介紹的校驗(yàn)函數(shù),可以直接執(zhí)行 full_clean(),它會按照順序挨個(gè)調(diào)用 clean_fields、clean、validate_unique
full_clean() 接收兩個(gè)參數(shù),一個(gè) exclude,接收不校驗(yàn)的字段列表,一個(gè)validate_unique,為 布爾型數(shù)據(jù),確定是否需要進(jìn)行唯一性校驗(yàn)
5. 重寫save()
前面我們會在每次進(jìn)行 save() 前都手動執(zhí)行校驗(yàn)函數(shù),我們可以重寫 save(),這樣在每次創(chuàng)建和保存一個(gè) model 實(shí)例的時(shí)候,就不需要手動調(diào)用我們前面的校驗(yàn)函數(shù)了,操作示例如下:
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() 可以直接調(diào)用這三個(gè)校驗(yàn)函數(shù),所以上面等效于:
def save(self, *args, **kwargs):
self.full_clean()
super(TestValidate, self).save(*args, **kwargs)
清空表數(shù)據(jù)后,我們可以進(jìn)行下面的測試:
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") # 會報(bào)校驗(yàn)的錯(cuò)誤
test_3 = TestValidate()
test_3.even_field=3
test_3.name = "def"
test_3.save() # 報(bào)校驗(yàn)的錯(cuò)誤
6. 繼承BaseModel
我們上面的操作是基于單個(gè) model 的 save() 操作,如果我們要對每個(gè) model 都實(shí)現(xiàn)這種自動進(jìn)行校驗(yàn)的操作,那么則需要對每個(gè) model 都進(jìn)行這種 save() 的繼承重寫操作
如果想要實(shí)現(xiàn)每個(gè) model 自動實(shí)現(xiàn)這種校驗(yàn)的操作,我們可以編寫一個(gè) BaseModel,在 BaseModel 里重寫 save() 操作,然后每個(gè) model 都繼承 BaseModel,就可以實(shí)現(xiàn)我們的目的了。
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)
這種操作也可以用于添加基礎(chǔ)字段,比如我們想為每個(gè) model 都添加 updated_time 和 created_time 作為基礎(chǔ)字段,用于記錄數(shù)據(jù)寫入和更新時(shí)間,可以在 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、系統(tǒng)驗(yàn)證器介紹
前面介紹的是自定義的驗(yàn)證器,以及如何使用,其實(shí)Django系統(tǒng)里為我們實(shí)現(xiàn)了很多基礎(chǔ)的驗(yàn)證器:
- EmailValidator 驗(yàn)證郵箱格式
- MaxValueValidator 驗(yàn)證最大值
- MinValueValidator 驗(yàn)證最小值
- MaxLengthValidator 驗(yàn)證最大長度
- MinLengthValidator 驗(yàn)證最小長度
- RegexValidator 驗(yàn)證正則表達(dá)式
我們設(shè)置的 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="")
用上了上面這幾個(gè)自帶的驗(yàn)證器,接下來我們對 email,count,char_str,telephone 幾個(gè)字段設(shè)置不合法的值進(jìn)行創(chuàng)建:
TestValidate.objects.create(
even_field=2,
name="abc",
email="12314234",
count=25,
char_str="abcd",
telephone="122282883"
)
然后會發(fā)現(xiàn)從 email 到 telephone 字段都報(bào)了字段值不合法的錯(cuò),我們將其挨個(gè)修正為 model 里符合校驗(yàn)規(guī)則的數(shù)據(jù)即可正常創(chuàng)建數(shù)據(jù)了。
如果想獲取更多相關(guān)文章,可掃碼關(guān)注閱讀:文章來源:http://www.zghlxwxcb.cn/news/detail-746753.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-746753.html
到了這里,關(guān)于Django筆記四十二之model使用validator驗(yàn)證器的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!