本文介绍如何使用 Pydantic 的 @field_validator(mode="before") 在嵌套模型(如 Dict[str, BaseModel])验证前,自动将字典键赋值给子模型的指定字段(如 type),避免手动预处理数据,实现声明式、可复用的数据增强逻辑。
本文介绍如何使用 pydantic 的 `@field_validator(mode="before")` 在嵌套模型(如 `dict[str, basemodel]`)验证前,自动将字典键赋值给子模型的指定字段(如 `type`),避免手动预处理数据,实现声明式、可复用的数据增强逻辑。
在 Pydantic 中,当嵌套结构依赖外部上下文(例如字典的 key 需映射为子模型的某个必填字段)时,标准验证流程会在子模型实例化前就失败——因为 UserType.type 缺失且无默认值。此时,不能依赖 model_post_init(它发生在所有字段已验证并实例化之后),而应选择 验证前钩子(pre-validation hook)。
最简洁、符合 Pydantic v2 设计哲学的方案是:对目标字段(如 objects)应用 @field_validator(..., mode="before")。该装饰器接收原始输入数据(未解析的 dict 或 None),允许你原地修改或重构数据结构,再交由后续验证流程处理。
以下是一个完整可运行示例:
from typing import Dictfrom pydantic import BaseModel, Field, field_validator, ValidationErrorclass UserType(BaseModel): name: str = Field(min_length=1) type: str = Field(min_length=1) # 现在可由父级自动注入class AppConfig(BaseModel): key1: int = Field(gt=0) objects: Dict[str, UserType] @field_validator("objects", mode="before") @classmethod def inject_type_from_key(cls, objects_dict): if not isinstance(objects_dict, dict): return objects_dict # 遍历每个 {key: value},将 key 注入 value 字典的 "type" 字段 for obj_key, obj_data in objects_dict.items(): if isinstance(obj_data, dict): obj_data["type"] = obj_key # ✅ 动态注入 return objects_dict# 测试数据:无需显式提供 "type"data = { "key1": 1, "objects": { "type1": {"name": "Name 2"}, "type2": {"name": "Name 1"} }}try: config = AppConfig.model_validate(data) # 推荐使用 model_validate() 替代(**data) print(config.model_dump_json(indent=2))except ValidationError as e: print(e)
输出结果:
{ "key1": 1, "objects": { "type1": { "name": "Name 2", "type": "type1" }, "type2": { "name": "Name 1", "type": "type2" } }}
✅ 关键要点说明:
⚠️ 注意事项:
通过该模式,你可在模型定义中集中管理上下文敏感的数据转换逻辑,提升代码可读性、可测试性与可维护性,真正实现“验证即转换”的声明式开发体验。