如何在Pydantic嵌套模型中使用子类?
解决方案
要让Pydantic模型支持接受基类的所有子类并在序列化时完整保留子类属性,最可靠的方式是使用**鉴别器(Discriminator)**实现多态类型处理,具体步骤如下:
1. 给基类添加鉴别器字段
在基类Action中定义一个用于区分子类的标识字段(比如type),通过Field(discriminator=True)标记它为鉴别器。每个子类指定唯一的type值,让Pydantic能够识别不同的子类类型。
2. 在父模型中使用带鉴别器的Union类型
在Alert的action字段中,使用Annotated[Union[所有Action子类], Field(discriminator="type")],这样既可以接受所有继承自Action的子类,序列化时也会完整保留子类的所有属性。如果不想手动列举所有子类,可以通过Action.__subclasses__()动态收集已定义的子类(需确保子类在Alert定义前被加载)。
完整代码示例
from pydantic import BaseModel, Field from typing import Annotated, Union # 基类:定义鉴别器字段 class Action(BaseModel): name: str type: str = Field(..., discriminator=True) # 子类1:指定唯一的type标识 class LogAction(Action): type: str = "log" log_level: str timestamp: str # 子类2:新增的Action子类无需修改Alert即可被支持 class NotifyAction(Action): type: str = "notify" channel: str recipient: str # 动态收集所有Action子类(可选,替代手动列举) ActionSubclasses = tuple(Action.__subclasses__()) class Alert(BaseModel): id: int message: str # 使用动态收集的子类集合,或手动写Union[Action, LogAction, NotifyAction] action: Annotated[Union[ActionSubclasses], Field(discriminator="type")] # 测试LogAction实例 alert1 = Alert( id=1, message="Alert Message", action=LogAction(name="Error Log", log_level="ERROR", timestamp="2024-04-20T10:45:00"), ) print(alert1) print(alert1.model_dump()) # 测试NotifyAction实例 alert2 = Alert( id=2, message="Notification Alert", action=NotifyAction(name="Send Email", channel="email", recipient="user@example.com"), ) print(alert2.model_dump())
输出结果
执行后,model_dump()会完整输出子类的所有属性:
Alert(id=1, message='Alert Message', action=LogAction(name='Error Log', type='log', log_level='ERROR', timestamp='2024-04-20T10:45:00')) {'id': 1, 'message': 'Alert Message', 'action': {'name': 'Error Log', 'type': 'log', 'log_level': 'ERROR', 'timestamp': '2024-04-20T10:45:00'}} {'id': 2, 'message': 'Notification Alert', 'action': {'name': 'Send Email', 'type': 'notify', 'channel': 'email', 'recipient': 'user@example.com'}}
关键说明
- 鉴别器字段(这里是
type)会被Pydantic用来判断具体的子类类型,序列化和反序列化时都会自动处理。 - 新增
Action子类时,只需指定唯一的type值,无需修改Alert类的定义(使用动态收集子类的方式时)。 - 如果不需要显式的鉴别器字段,也可以通过
model_config中的discriminator参数指定内部标识,但显式定义的方式更直观易维护。
内容的提问来源于stack exchange,提问作者Dronakuul
相关产品推荐
相关产品推荐

