1.装饰器实现验证器
from pydantic import BaseModel, ValidationError, validator
class UserModel(BaseModel):
name: str
username: str
password1: str
password2: str
@validator('name')
def name_must_contain_space(cls, v):
if ' ' not in v:
raise ValueError('must contain a space')
return v.title()
@validator('password2')
def passwords_match(cls, v, values, **kwargs):
# values:包含任何先前验证的字段的名称到值映射的字典
if 'password1' in values and v != values['password1']:
raise ValueError('passwords do not match')
return v
user = UserModel(
name='samuel colvin',
username='scolvin',
password1='zxcvbn',
password2='zxcvbn',
)
print(user)
# name='Samuel Colvin' username='scolvin' password1='zxcvbn' password2='zxcvbn'
try:
UserModel(
name='samuel',
username='scolvin',
password1='zxcvbn',
password2='zxcvbn2',
)
except ValidationError as e:
print(e)
"""
2 validation errors for UserModel
name
must contain a space (type=value_error)
password2
passwords do not match (type=value_error)
"""
2.验证器的复杂校验
pre=True
:前置操做,最先执行的验证器
each_item=True
:字段遍历:如果所有验证器都没有pre=True
,则该验证器最先执行,否则紧跟着具有pre=True
的验证器
from typing import List
from pydantic import BaseModel, ValidationError, validator
class DemoModel(BaseModel):
square_numbers: List[int] = []
cube_numbers: List[int] = []
# 1.可以使用'*'来校验所有字段
# 2.pre=True,可以在验证器前对数据进行预处理,此处如果不处理字符串,下面的验证器在对字段值求和时就会报错
@validator('*', pre=True)
def split_str(cls, v):
if isinstance(v, str):
return v.split('|')
return v
# 3.没有初始化的字段,验证器不会执行
@validator('cube_numbers', 'square_numbers')
def check_sum(cls, v):
if sum(v) > 42:
raise ValueError('sum of numbers greater than 42')
return v
# 4.each_item=True可以实现对字段值的遍历
@validator('square_numbers', each_item=True)
def check_squares(cls, v):
print(v)
assert v >= 1, f'{v} is not a square number'
return v
print(DemoModel(square_numbers="1|2|3"))
# print(DemoModel(square_numbers=[1, 4, 9]))
3.子类验证器和each_item
- 如果将验证程序与引用父类上的类型字段的子类一起使用,将导致验证程序不运行;相反,必须以编程方式对列表进行迭代。
each_item=True
from typing import List
from pydantic import BaseModel, ValidationError, validator
class ParentModel(BaseModel):
names: List[str]
class ChildModel(ParentModel):
# 每个元素必须为空字符串
@validator('names', each_item=True)
def check_names_not_empty(cls, v):
assert v != '', 'Empty strings are not allowed.'
return v
# This will NOT raise a ValidationError because the validator was not called
try:
child = ChildModel(names=['Alice', 'Bob', 'Eve', ''])
except ValidationError as e:
print(e)
else:
print('No ValidationError caught.')
# No ValidationError caught.
class ChildModel2(ParentModel):
@validator('names')
def check_names_not_empty(cls, v):
for name in v:
assert name != '', 'Empty strings are not allowed.'
return v
try:
child = ChildModel2(names=['Alice', 'Bob', 'Eve', ''])
except ValidationError as e:
print(e)
"""
1 validation error for ChildModel2
names
Empty strings are not allowed. (type=assertion_error)
"""
4.始终验证
- 验证器在校验字段没有初始化的前提下是不会进行校验的
- 使用
always=True
来实现,在没有初始化的前提下,给提供一个动态的默认值
- 通常情况下与
pre=True
一起使用
from datetime import datetime
from pydantic import BaseModel, validator
class DemoModel(BaseModel):
ts: datetime = None
@validator('ts', pre=True, always=True)
def set_ts_now(cls, v):
return v or datetime.now()
print(DemoModel())
# ts=datetime.datetime(2022, 6, 15, 18, 25, 41, 389042)
5.重用验证器
from pydantic import BaseModel, validator
def normalize(name: str) -> str:
return ' '.join((word.capitalize()) for word in name.split(' '))
class Producer(BaseModel):
name: str
# 参数需要和model中需要校验的字段保持一致
# 参数需要和验证器normalize中的形参保持一致
# 返回值必须提供变量进行接收
_normalize_p_name = validator('name', allow_reuse=True)(normalize)
class Consumer(BaseModel):
name: str
# validators
_normalize_c_name = validator('name', allow_reuse=True)(normalize)
jane_doe = Producer(name='JaNe DOE')
print(jane_doe.name)
john_doe = Consumer(name='joHN dOe')
print(john_doe.name)
assert jane_doe.name == 'Jane Doe'
assert john_doe.name == 'John Doe'
6.根验证器
from pydantic import BaseModel, ValidationError, root_validator
class UserModel(BaseModel):
username: str
password1: str
password2: str
@root_validator(pre=True)
def check_card_number_omitted(cls, values):
assert 'card_number' not in values, 'card_number should not be included'
return values
@root_validator
def check_passwords_match(cls, values):
pw1, pw2 = values.get('password1'), values.get('password2')
if pw1 is not None and pw2 is not None and pw1 != pw2:
raise ValueError('passwords do not match')
return values
print(UserModel(username='scolvin', password1='zxcvbn', password2='zxcvbn'))