
本文介绍如何使用 Pydantic v2 的 @model_validator(mode="after") 实现嵌套字段的条件校验——当主类别(如 meat/veg)确定后,自动限制子类别只能从对应预定义枚举中选取,确保数据结构强类型且业务逻辑内聚。
本文介绍如何使用 pydantic v2 的 `@model_validator(mode="after")` 实现嵌套字段的条件校验——当主类别(如 `meat`/`veg`)确定后,自动限制子类别只能从对应预定义枚举中选取,确保数据结构强类型且业务逻辑内聚。
在构建具有层级依赖关系的业务模型(如商品分类体系)时,简单的枚举嵌套无法表达“category=meat 时 sub_category 只能是 beef 或 lamb”这类动态约束。Pydantic 本身不支持跨字段的枚举联动,但可通过 @model_validator(mode="after") 在实例化后进行语义校验,实现精准、可维护的条件验证逻辑。
以下是一个完整可运行的示例:
from enum import Enum
from pydantic import BaseModel, model_validator
from typing import Union
class Category(str, Enum):
meat = "meat"
veg = "veg"
class MeatSubCategory(str, Enum):
beef = "beef"
lamb = "lamb"
class VegSubCategory(str, Enum):
carrot = "carrot"
potato = "potato"
# 使用 Union 构建联合类型,兼容两类子枚举
SubCategory = Union[MeatSubCategory, VegSubCategory]
class ProductCategory(BaseModel):
category: Category
sub_category: SubCategory
@model_validator(mode="after")
def validate_sub_category(self) -> "ProductCategory":
# 定义每个主类对应的合法子类枚举映射
valid_subcategories = {
Category.meat: MeatSubCategory,
Category.veg: VegSubCategory,
}
expected_enum = valid_subcategories.get(self.category)
# 检查 sub_category 是否属于预期枚举类型(注意:isinstance 对 str 枚举有效)
if not isinstance(self.sub_category, expected_enum):
raise ValueError(
f"Sub-category '{self.sub_category}' is not valid for category '{self.category}'"
)
return self
✅ 关键要点说明:
- Union[MeatSubCategory, VegSubCategory] 允许 sub_category 接收任一子枚举值,同时保留类型提示完整性;
- @model_validator(mode="after") 在所有字段解析完成后执行,此时 self.category 和 self.sub_category 均已为 Python 原生对象(如 Category.meat 和 "beef" 字符串),可直接比对;
- isinstance(value, EnumClass) 是校验枚举成员归属的可靠方式(优于字符串匹配,避免拼写误判);
- 映射字典 valid_subcategories 易于扩展——新增类别只需添加枚举类 + 更新映射,无需修改校验逻辑。
⚠️ 注意事项:
- 若 sub_category 传入非法字符串(如 "tofu"),Pydantic 默认会先尝试转换为 SubCategory 类型;若失败则抛出 ValidationError(提示“Input should be 'beef', 'lamb', 'carrot' or 'potato'”)。而我们的 model_validator 负责第二层业务规则校验——即“即使字符串合法,也必须匹配当前 category”。两者互补,不可替代;
- 不建议在 @field_validator 中做跨字段校验,因其仅接收单个字段值,无法访问 category;
- 如需支持更多类别(如 dairy, fruit),只需定义新 Enum 类并扩展 valid_subcategories 字典即可,保持高内聚低耦合。
通过该模式,你不仅能保障 API 输入的数据合规性,还能在 IDE 中获得完整的类型提示与静态检查支持,真正实现“用代码表达业务契约”。











