【Python】在 Pydantic 模型中使用非 Pydantic 定义的类作为模型字段类型
文章目录
- 前言
- 模型字段为对象类型时
- 模型字段为对象列表类型时
前言
在 Pydantic
模型中使用非 Pydantic
定义的类作为模型字段类型时,核心问题是让 Pydantic
能够正确解析输入数据(如 JSON
/字典)为自定义类的实例,并可选地序列化回原始格式。
场景假设:假设我们有一个普通 Python
类 User
(非 Pydantic
模型):
# 自定义类,非 Pydantic 模型
class User:def __init__(self, name: str, age: int):self.name = nameself.age = agedef __repr__(self):return f"User(name={self.name}, age={self.age})"
Pydantic v2
支持 BeforeValidator
,可以在数据被解析到模型字段前进行预处理,适合需要对原始输入做复杂转换的场景。
模型字段为对象类型时
步骤说明
- 导入
BeforeValidator
和Annotated
。 - 定义预处理函数(如将字典转为
Use
实例)。 Pydantic
模型设置arbitrary_types_allowed=True
,表明可以包含任意类型的字段,比如自定义类、第三方库的对象等。否则,Pydantic
默认只允许标准类型和已注册的类型。- 使用
Annotated[User, BeforeValidator(preprocess_func)]
标记字段类型。
from typing import Annotatedfrom pydantic import BaseModel, BeforeValidator# 预处理函数:将输入转为 User 实例
def preprocess_user(value: dict) -> User:if not isinstance(value, dict):raise ValueError("Expected dict for User")return User(**value)# Pydantic 模型(使用 Annotated 绑定 BeforeValidator)
class Profile(BaseModel, arbitrary_types_allowed=True):user_info: Annotated[User, BeforeValidator(preprocess_user)]if __name__ == '__main__':# 测试:从字典解析data = {"user_info": {"name": "Diana", "age": 28}}profile = Profile(**data)print(profile.user_info) # 输出:User(name=Diana, age=28)
模型字段为对象列表类型时
与模型字段为对象类型时的处理方式大同小异
from typing import List, Annotatedfrom pydantic import BaseModel, BeforeValidator# 预处理函数:将字典列表转为 User 实例列表
def preprocess_user_list(value: List[dict]) -> List[User]:user_infos = []for idx, item in enumerate(value):if not isinstance(item, dict):raise ValueError(f"Element {idx} in user_infos is not a dict")user_infos.append(User(**item))return user_infos# Pydantic 模型(使用 Annotated 绑定 BeforeValidator)
class Profile(BaseModel, arbitrary_types_allowed=True):user_infos: Annotated[List[User], BeforeValidator(preprocess_user_list)]# 配置序列化(同上)model_config = {"json_encoders": {User: lambda u: {"name": u.name, "age": u.age}}}if __name__ == '__main__':# 测试:从字典列表解析data = {"user_infos": [{"name": "Diana", "age": 28},{"name": "Eve", "age": 22}]}profile = Profile(**data)print(profile.user_infos) # 输出:[User(name=Diana, age=28), User(name=Eve, age=22)]