简介
python类型注释的数据校验和设置管理 pydantic在运行时强制执行类型提示,并在数据无效时提供友好的错误 换句话说,pydantic保证输出模型的类型和约束,而不是输入数据 在pydantic中定义对象的主要方法是通过继承自BaseModel类的模型 官方文档:https://pydantic-docs.helpmanual.io/
QuickStart
安装
pip install pydantic
case ```python from datetime import datetime from typing import List, Optional from pydantic import BaseModel, ValidationError
class User(BaseModel): id: int name = “zaygee” signup_ts: Optional[datetime] = None friends: List[int] = []
external_data = { ‘id’: ‘123’, ‘signup_ts’: ‘2019-06-01 12:22’, ‘friends’: [1, 2, ‘3’], }
字典解包
user = User(**external_data) print(user.id) # 123
try: User(signup_ts = “broken”, friends=[1, 2, “not number”]) except ValidationError as e: print(e.json())
[
{
“loc”: [
“id”
],
“msg”: “field required”,
“type”: “value_error.missing”
},
{
“loc”: [
“signup_ts”
],
“msg”: “invalid datetime format”,
“type”: “value_error.datetime”
},
{
“loc”: [
“friends”,
2
],
“msg”: “value is not a valid integer”,
“type”: “type_error.integer”
}
]
<a name="LydxY"></a>### 模型&模型属性基本模型继承自类 BaseModel<a name="GsCif"></a>#### dict(): 返回模型字段和值的字典> - include: 包含在返回字典中的字段> - exclude:要从返回的字典中排除的字段> - by_alias: 是否应使用字段别名作为返回字典中的键> - exclude_unset: 创建模型时未明确设置的字段是否应从返回的字典中排除, 默认 False> - exclude_defaults:是否应从返回的字典中排除等于其默认值(无论是否设置)的字段,默认 False> - exclude_none: 是否None应该从返回的字典中排除等于的字段;默认 False```pythonprint("返回模型字段和值的字典", user.dict(include={"name"}, exclude_unset=True)) # {'name': 'zaygee'}print(user.dict(exclude_defaults=True))print(user.dict(exclude_defaults=False))
json(): 会将模型序列化为 JSON
- include: 包含在返回字典中的字段
- exclude:要从返回的字典中排除的字段
- by_alias: 是否应使用字段别名作为返回字典中的键
- exclude_unset: 创建模型时未明确设置的字段是否应从返回的字典中排除, 默认 False
- exclude_defaults:是否应从返回的字典中排除等于其默认值(无论是否设置)的字段,默认 False
- exclude_none: 是否None应该从返回的字典中排除等于的字段;默认 False
- encoder:传递给default参数的自定义编码器函数json.dumps();默认为自定义编码器,旨在处理所有常见类型
- dumps_kwargs: 任何其他关键字参数都传递给json.dumps(),例如indent
print("返回json字符串表示dict():", user.json(indent=4), type(user.json()))
copy(): 浅复制模型副本
- 典中的字段
- exclude:要从返回的字典中排除的字段
- update:创建复制模型时要更改的值字典
- deep: 是否对新模型进行深拷贝;默认False
print("浅复制模型副本:", user.copy(include={"name", "id"})) # id=123 name='zaygee1'print(user.copy(update={"name": "update_name"})) # id=123 signup_ts=datetime.datetime(2019, 6, 1, 12, 22) friends=[1, 2, 3] name='update_name'print("排除字段exclude:", user.copy(exclude={"name", "signup_ts"})) # id=123 friends=[1, 2, 3]print("deep深复制:", user.copy(deep=True))
parse_obj(): 如果对象不是字典,则用于将任何对象加载到模型中并进行错误处理的实用程序
print(user.parse_obj({"id": 123,"signup_ts":1234567890,"name":"John Doe"}) ) # id=123 signup_ts=datetime.datetime(2009, 2, 13, 23, 31, 30, tzinfo=datetime.timezone.utc) friends=[] name='John Doe'# print(user.parse_obj('{"id": 123,"signup_ts":1234567890,"name":"John Doe"}') ) # validation error for User
parse_raw(): 用于加载多种格式的字符串的实用程序
print(user.parse_raw('{"id": 123,"signup_ts":1234567890,"name":"John Doe"}'))
parse_file(): 就像parse_raw()但对于文件路径
schema(): 返回将模型表示为 JSON 模式的字典
print("-"*50)print(user.schema())print("-"*50)# {'title': 'User', 'type': 'object', 'properties': {'id': {'title': 'Id', 'type': 'integer'}, 'signup_ts': {'title': 'Signup Ts', 'type': 'string', 'format': 'date-time'}, 'friends': {'title': 'Friends', 'default': [], 'type': 'array', 'items': {'type': 'integer'}}, 'name': {'title': 'Name', 'default': 'zaygee', 'type': 'string'}}, 'required': ['id']}
schema_json(): 返回 JSON 字符串表示schema()
print("-"*50)print(user.schema_json())print("-"*50)# --------------------------------------------------# {"title": "User", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "signup_ts": {"title": "Signup Ts", "type": "string", "format": "date-time"}, "friends": {"title": "Friends", "default": [], "type": "array", "items": {"type": "integer"}}, "name": {"title": "Name", "default": "zaygee", "type": "string"}}, "required": ["id"]}# --------------------------------------------------
fields_set: 初始化模型实例时设置的字段名称集
print(user.__fields_set__)# {'name', 'signup_ts', 'friends', 'id'}
fields: 模型字段的字典
print(user.__fields__)# {'id': ModelField(name='id', type=int, required=True), 'signup_ts': ModelField(name='signup_ts', type=Optional[datetime], required=False, default=None), 'friends': ModelField(name='friends', type=List[int], required=False, default=[]), 'name': ModelField(name='name', type=str, required=False, default='zaygee')}
错误处理
字段类型校验
必填字段Field(…)&可选字段Optional[]
from pydantic import Fieldfrom typing import Optional# a、b、c都是必选的,但是c可填Noneclass Model(BaseModel):a: intc: int = Field(...)d: Optional[int] = Field(...)print(Model(a=0, c=2, d=None))
parse_obj_as将字段解析为指定类型
from typing import Listfrom pydantic import parse_obj_asclass Item(BaseModel):id: intname: stritem_data = [{'id': 1, 'name': 'My Item'}]items = parse_obj_as(List[Item], item_data)print(items, type(items))for index, key in items:print(index, key)print(index[-1], key[-1])# [Item(id=1, name='My Item')] <class 'list'># ('id', 1) ('name', 'My Item')# 1 My Item
字段类型约束
from decimal import Decimalfrom pydantic import (BaseModel,NegativeFloat,NegativeInt,PositiveFloat,PositiveInt,NonNegativeFloat,NonNegativeInt,NonPositiveFloat,NonPositiveInt,conbytes,condecimal,confloat,conint,conlist,conset,constr,Field,ByteSize,HttpUrl,SecretStr,FilePath,Json)class AllModel(BaseModel):file_path: FilePath # 文件校验url: Optional[HttpUrl] # 网站校验password: SecretStr # 秘文lower_str: constr(to_lower=True, min_length=1, max_length=10) # con*的约束类型size: ByteSize # 字节大小class ComplexJsonModel(BaseModel):json_obj: Json[List[int]]print(ComplexJsonModel(json_obj='[1, 2, 3]'))print(AllModel(file_path="Script/python_test/base/test_pydantic.py",url="https://www.baidu.com",password="testpw",lower_str="SSS",size=520))#file_path=PosixPath('Script/python_test/base/test_pydantic.py') url=HttpUrl('https://www.baidu.com', scheme='https', host='www.baidu.com', tld='com', host_type='domain') password=SecretStr('**********') lower_str='sss' size=520
验证器-todo
pre & per-item验证器
验证器 & each_item
始终验证
重用验证器
数据类验证器
配置文件加载
from typing import Setfrom pydantic import (BaseModel,BaseSettings,PyObject,RedisDsn,PostgresDsn,Field,)class SubModel(BaseModel):foo = 'bar'apple = 1class Settings(BaseSettings):auth_key: strapi_key: str = Field(..., env='my_api_key')redis_dsn: RedisDsn = 'redis://user:pass@localhost:6379/1'pg_dsn: PostgresDsn = 'postgres://user:pass@localhost:5432/foobar'special_function: PyObject = 'math.cos'# to override domains:# export my_prefix_domains='["foo.com", "bar.com"]'domains: Set[str] = set()# to override more_settings:# export my_prefix_more_settings='{"foo": "x", "apple": 1}'more_settings: SubModel = SubModel()class Config:env_prefix = 'my_prefix_' # defaults to no prefix, i.e. ""fields = {'auth_key': {'env': 'my_auth_key',},'redis_dsn': {'env': ['service_redis_dsn', 'redis_url']}}print(Settings(auth_key="xxx", api_key="xxx").dict())#{'auth_key': 'xxx', 'api_key': 'xxx', 'redis_dsn': RedisDsn('redis://user:pass@localhost:6379/1', scheme='redis', user='user', password='pass', host='localhost', host_type='int_domain', port='6379', path='/1'), 'pg_dsn': PostgresDsn('postgres://user:pass@localhost:5432/foobar', scheme='postgres', user='user', password='pass', host='localhost', host_type='int_domain', port='5432', path='/foobar'), 'special_function': <built-in function cos>, 'domains': set(), 'more_settings': {'foo': 'bar', 'apple': 1}}
结合dotenv加载配置文件
安装dotenv
# 安装dotenvpip install python-dotenv# 新建配置文件touch .env---------------------# .env 文件# ignore commentENVIRONMENT="production"REDIS_ADDRESS=localhost:6379APP_NAME='Hello world'---------------------# 结合Dotenv文件设置变量from pydantic import BaseSettingsclass Settings(BaseSettings):app_name: str = "Awesome API"environment: stritems_per_user: int = 50class Config:env_file = '.env'env_file_encoding = "utf-8"settings = Settings(_env_file='/Users/zaygee/work_script/.env', _env_file_encoding='utf-8')print(settings) # app_name='Hello world' environment='production' redis_address='localhost:6379'
