Pydantic可以在程式碼執行時提供型別提示, 資料校驗失敗時提供友好的錯誤提示, 使用Python的型別註解來進行資料校驗和settings管理
一般使用
from datetime import datetime
from typing import List
from typing import Optional
from pydantic import BaseModel
# 1 定義模型
class User(BaseModel):
id: int # 必須欄位
name: str = "John Snow" # 有預設值,選填欄位
signup_ts: Optional[datetime] = None
friends: List[int] = [] # 列表中元素是int型別或者可以直接轉換成int型別
external_data = {
"id": "123",
"signup_ts": "2020-12-22 12:22",
"friends": [1, 2, "3"], # "3"是可以int("3")的
}
# 2 檢驗資料
user = User(**external_data)
# 3 訪問資料
print(user.id, user.friends) # 例項化後呼叫屬性
print(repr(user.signup_ts))
# .dict 方法, 返回資料字典
print(user.dict())
校驗失敗處理
假如資料檢驗不通過, 會丟擲pydantic.ValidationError
from datetime import datetime
from typing import List
from typing import Optional
from pydantic import BaseModel, ValidationError
class User(BaseModel):
id: int # 必須欄位
name: str = "John Snow" # 有預設值,選填欄位
signup_ts: Optional[datetime] = None
friends: List[int] = [] # 列表中元素是int型別或者可以直接轉換成int型別
external_data = {
"id": "123",
"signup_ts": "2020-12-22 12:22",
"friends": [1, 2, "3"], # "3"是可以int("3")的
}
try:
User(id=1, signup_ts=datetime.today(), friends=[1, 2, "not number"])
except ValidationError as e:
print(e.json())
"""
[
{
"loc": [
"friends",
2
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
"""
模型類的的屬性和方法
from datetime import datetime
from pathlib import Path
from typing import List
from typing import Optional
from pydantic import BaseModel
class User(BaseModel):
id: int # 必須欄位
name: str = "John Snow" # 有預設值,選填欄位
signup_ts: Optional[datetime] = None
friends: List[int] = [] # 列表中元素是int型別或者可以直接轉換成int型別
external_data = {
"id": "123",
"signup_ts": "2020-12-22 12:22",
"friends": [1, 2, "3"], # "3"是可以int("3")的
}
user = User(**external_data)
# 獲得已檢驗資料的字典資料
print(user.dict())
# 獲得已檢驗資料的json資料
print(user.json())
# 這裡是淺拷貝
print(user.copy())
# 通過物件解析
print(User.parse_obj(external_data))
# 通過字串解析
print(User.parse_raw('{"id": "123", "signup_ts": "2020-12-22 12:22", "friends": [1, 2, "3"]}'))
path = Path('pydantic_tutorial.json')
path.write_text('{"id": "123", "signup_ts": "2020-12-22 12:22", "friends": [1, 2, "3"]}')
# 通過文字解析
print(User.parse_file(path))
# 獲得物件 概要
# {'title': 'User', 'type': 'object', 'properties': {'id': ...} }
print(user.schema())
# 獲得物件 概要json
# {'title': 'User', 'type': 'object', 'properties': {'id': ...} }
print(user.schema_json())
user_data = {"id": "error", "signup_ts": "2020-12-22 12 22", "friends": [1, 2, 3]} # id是字串 是錯誤的
# 不檢驗資料直接建立模型類,不建議在construct方法中傳入未經驗證的資料
print(User.construct(**user_data))
# 獲得所有欄位
# 定義模型類的時候,所有欄位都註明型別,欄位順序就不會亂
print(User.__fields__.keys())
模型巢狀
from datetime import datetime, date
from typing import List
from typing import Optional
from pydantic import BaseModel
class User(BaseModel):
id: int # 必須欄位
name: str = "John Snow" # 有預設值,選填欄位
signup_ts: Optional[datetime] = None
friends: List[int] = [] # 列表中元素是int型別或者可以直接轉換成int型別
external_data = {
"id": "123",
"signup_ts": "2020-12-22 12:22",
"friends": [1, 2, "3"], # "3"是可以int("3")的
}
user = User(**external_data)
class Sound(BaseModel):
sound: str
class Dog(BaseModel):
birthday: date
weight: float = Optional[None]
sound: List[Sound] # 不同的狗有不同的叫聲。遞迴模型(Recursive Models)就是指一個巢狀一個
dogs = Dog(birthday=date.today(), weight=6.66, sound=[{"sound": "wang wang ~"}, {"sound": "ying ying ~"}])
print(dogs.dict())
與ORM結合
from datetime import datetime
from typing import List
from typing import Optional
from pydantic import BaseModel
from pydantic import constr
from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class CompanyOrm(Base):
__tablename__ = 'companies'
id = Column(Integer, primary_key=True, nullable=False)
public_key = Column(String(20), index=True, nullable=False, unique=True)
name = Column(String(63), unique=True)
domains = Column(ARRAY(String(255)))
class CompanyModel(BaseModel):
id: int
# constr用於約束字串
public_key: constr(max_length=20)
name: constr(max_length=63)
domains: List[constr(max_length=255)]
class Config:
orm_mode = True
co_orm = CompanyOrm(
id=123,
public_key='foobar',
name='Testing',
domains=['example.com', 'foobar.com'],
)
print(CompanyModel.from_orm(co_orm))
驗證器
如何使用及引數見passwords_match
方法
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, **kwargs):
if ' ' not in v:
raise ValueError('must contain a space')
return v.title()
@validator('password2')
def passwords_match(cls, v, values, **kwargs):
"""
:param v: 當前欄位的值: zxcvbn2
:param values: 已經驗證的資料: {'username': 'scolvin', 'password1': 'zxcvbn'}
:param kwargs: {'field': ModelField(name='password2', type=str, required=True),
'config': <class '__main__.Config'>}
:return:
"""
if 'password1' in values and v != values['password1']:
raise ValueError('passwords do not match')
return v
@validator('username')
def username_alphanumeric(cls, v):
assert v.isalnum(), 'must be alphanumeric'
return v
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)
"""
全部欄位型別
見官方文件: Field Types
更多其他使用方法見: pydantic-docs