pydantic是python进行类型提示和数据验证的一个库,要学习fastapi
必须先了解这个库,
额外会用到的工具
pip install pydantic
pip install pydantic[email,dotenv] # 邮箱和环境变量的验证的时候使用
除了知道的int,str,list,dict,float
等之外,还可以提示
datetime,会将unix timestamp int (e.g. 1496498400)
or a string 转为日期和时间.
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel
class User(BaseModel):
id: int
name = 'John Doe'
signup_ts: Optional[datetime] = None
friends: List[int] = []
使用get_secret_value method
方法看到密码,也可以只配置到json中,通过json查看
from pydantic import BaseModel, SecretStr, SecretBytes, ValidationError
class SimpleModel(BaseModel):
password: SecretStr
password_bytes: SecretBytes
sm = SimpleModel(password='IAmSensitive', password_bytes=b'IAmSensitiveBytes')
# Standard access methods will not display the secret
print(sm)
#> password=SecretStr('**********') password_bytes=SecretBytes(b'**********')
print(sm.password)
#> **********
print(sm.dict())
"""
{
'password': SecretStr('**********'),
'password_bytes': SecretBytes(b'**********'),
}
"""
print(sm.json())
#> {"password": "**********", "password_bytes": "**********"}
# Use get_secret_value method to see the secret's content.
print(sm.password.get_secret_value())
#> IAmSensitive
print(sm.password_bytes.get_secret_value())
#> b'IAmSensitiveBytes'
try:
SimpleModel(password=[1, 2, 3], password_bytes=[1, 2, 3])
except ValidationError as e:
print(e)
"""
2 validation errors for SimpleModel
password
str type expected (type=type_error.str)
password_bytes
byte type expected (type=type_error.bytes)
"""
# If you want the secret to be dumped as plain-text using the json method,
# you can use json_encoders in the Config class.
class SimpleModelDumpable(BaseModel):
password: SecretStr
password_bytes: SecretBytes
class Config:
json_encoders = {
SecretStr: lambda v: v.get_secret_value() if v else None,
SecretBytes: lambda v: v.get_secret_value() if v else None,
}
sm2 = SimpleModelDumpable(
password='IAmSensitive', password_bytes=b'IAmSensitiveBytes'
)
# Standard access methods will not display the secret
print(sm2)
#> password=SecretStr('**********') password_bytes=SecretBytes(b'**********')
print(sm2.password)
#> **********
print(sm2.dict())
"""
{
'password': SecretStr('**********'),
'password_bytes': SecretBytes(b'**********'),
}
"""
# But the json method will
print(sm2.json())
#> {"password": "IAmSensitive", "password_bytes": "IAmSensitiveBytes"}
开启arbitrary_types_allowed = True
,虽然Pet
中的name
是str类型,但是并不会进行验证
from pydantic import BaseModel, ValidationError
# This is not a pydantic model, it's an arbitrary class
class Pet:
def __init__(self, name: str):
self.name = name
class Model(BaseModel):
pet: Pet
owner: str
class Config:
arbitrary_types_allowed = True
__fields_set__
user = User(id='123')
print(user.__fields_set__ == {'id'}) #True
print(user.__fields_set__ == {'name'}) # False
在这里插入代码片
schema()
returns a dictionary representing the model as JSON Schemaschema_json()
returns a JSON string representation of schema()construct()
a class method for creating models without running validation__fields__
a dictionary of the model’s fields__config__
the configuration class for the modelBaseModel
场景:接口接收的参数需要有个param
字段,需要根据不通的接口传输不通的格式进行验证(这儿感觉表述不清楚)
import pydantic
class User(pydantic.BaseModel):
id: int
name: str
class Student(User):
semester: int
class Student_User(Student):
building: str
import pydantic
class User(pydantic.BaseModel):
id: int
name: str
class Student(User):
semester: int
class Student_User(Student):
building: str
print(Student_User.__fields__.keys())
model = pydantic.create_model("Student_User2", building=(str, ...), __base__=Student)
from pydantic import create_model, ValidationError, validator
def username_alphanumeric(cls, v):
assert v.isalnum(), 'must be alphanumeric'
return v
validators = {
'username_validator':
validator('username')(username_alphanumeric)
}
UserModel = create_model(
'UserModel',
username=(str, ...),
__validators__=validators
)
限定jobType
字段只能在给定的几个值中选择,使用@validator()
from pydantic import BaseModel, Field, validator
class ReceiveBase(BaseModel):
jobId: int = Field(..., gt=0, description="任务标识")
jobType: str = Field(..., description="任务类型"),
targetClass: str = Field(..., description="表示分析目标类型"),
targetId: int = Field(..., gt=0, description="目标标识"),
@validator("jobType")
def jobType_must_be_in_jobType_list(cls, jobType):
jobType_list = ["predict", "packing", "label", "anomaly"]
if jobType not in jobType_list:
raise ValueError(f'must be in {jobType_list}')
return jobType
from pydantic import BaseModel
class FooBarModel(BaseModel):
a: str
b: dict
class Config:
allow_mutation = False
foobar = FooBarModel(a='hello', b={'apple': 'pear'})
现在有这么一个问题,这三个BaseModel是互相套欠的
class Virt(BaseModel):
timestamp: str
value: int
class Params(BaseModel):
virt_data: List[Virt]
preLen: int
freq: str
class PredictReceive(ReceiveBase):
params: Params
接口是这样的
@prerouter.post('/getPredictInfo', response_model=PredictReturn)
def getPredictInfo(data: PredictReceive):
perf_data = data.params
jobType = data.jobType
targetId = data.targetId
virt = [i.dict() for i in perf_data.virt_data]
我们解析perf_data.virt_data
是
[Virt(timestamp='2021-01', value=20), Virt(timestamp='2021-02', value=30), Virt(timestamp='2021-03', value=15),]
list中都是Virt
对象,这种我们是没办法用的,我用的最粗鲁的办法是
virt = [i.dict() for i in perf_data.virt_data]
但是这种办法不优美,有没有其他更好的办法呢?
对不起,没有想到怎么搞,但是部分场景中可以参考这篇文章
pre=True
为先做这个验证
each_item=Tru
e 对LIST(或者底朝天,set等)中的每个元素处理
from typing import List
from pydantic import BaseModel, ValidationError, validator
class DemoModel(BaseModel):
square_numbers: List[int] = []
cube_numbers: List[int] = []
# '*' is the same as 'cube_numbers', 'square_numbers' here:
@validator('*', pre=True)
def split_str(cls, v):
if isinstance(v, str):
return v.split('|')
return v
@validator('cube_numbers', 'square_numbers')
def check_sum(cls, v):
if sum(v) > 42:
raise ValueError('sum of numbers greater than 42')
return v
@validator('square_numbers', each_item=True)
def check_squares(cls, v):
assert v ** 0.5 % 1 == 0, f'{v} is not a square number'
return v
@validator('cube_numbers', each_item=True)
def check_cubes(cls, v):
# 64 ** (1 / 3) == 3.9999999999999996 (!)
# this is not a good way of checking cubes
assert v ** (1 / 3) % 1 == 0, f'{v} is not a cubed number'
return v
print(DemoModel(square_numbers=[1, 4, 9]))
#> square_numbers=[1, 4, 9] cube_numbers=[]
print(DemoModel(square_numbers='1|4|16'))
#> square_numbers=[1, 4, 16] cube_numbers=[]
print(DemoModel(square_numbers=[16], cube_numbers=[8, 27]))
#> square_numbers=[16] cube_numbers=[8, 27]
always=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, 5, 19, 10, 49, 22, 520641)
print(DemoModel(ts='2017-11-08T14:00'))
#> ts=datetime.datetime(2017, 11, 8, 14, 0)
allow_reuse=True
想在多个字段/模型上使用同一个验证器
from pydantic import BaseModel, validator
def normalize(name: str) -> str:
return ' '.join((word.capitalize()) for word in name.split(' '))
class Producer(BaseModel):
name: str
# validators
_normalize_name = validator('name', allow_reuse=True)(normalize)