如何用Pydantic创建含固定字段与动态OtherModel字段的模型?
用Pydantic实现带固定字段+动态验证键值对的模型
需求说明
需要实现一个类似以下TypeScript接口的Pydantic模型:
interface ExpandedModel { fixed: number; [key: string]: OtherModel; }
核心要求:
- 必须包含必填固定字段
fixed(整数类型) - 所有额外的字符串键对应的字段值,都必须通过
OtherModel的验证规则
直接使用extra = "allow"无法满足需求,因为该配置仅允许额外字段存在,但不会对其进行类型验证:
class ExpandedModel(BaseModel): fixed: int class Config: extra = "allow"
尝试通过__root__特性定义动态键值对模型后,无法继承添加固定字段,会触发错误:
class VariableKeysModel(BaseModel): __root__: Dict[str, OtherModel] class ExpandedModel(VariableKeysModel): fixed: int
ValueError: root cannot be mixed with other fields
以下是三种可行的解决方案:
方案1:使用根验证器(Pydantic v1)
通过@root_validator手动分离固定字段与动态字段,并对动态字段逐一做OtherModel验证:
from pydantic import BaseModel, root_validator from typing import Dict, Any class OtherModel(BaseModel): value: str count: int class ExpandedModel(BaseModel): fixed: int _dynamic_fields: Dict[str, OtherModel] = {} @root_validator(pre=True) def split_and_validate(cls, values: Dict[str, Any]) -> Dict[str, Any]: # 提取并校验固定字段 fixed_value = values.pop("fixed", None) if fixed_value is None: raise ValueError("必填字段'fixed'缺失") # 验证所有剩余字段是否符合OtherModel规则 dynamic_fields = {} for key, val in values.items(): try: dynamic_fields[key] = OtherModel(**val) except Exception as e: raise ValueError(f"字段'{key}'不符合OtherModel格式: {str(e)}") return { "fixed": fixed_value, "_dynamic_fields": dynamic_fields } # 提供便捷方法获取动态字段 def get_dynamic_fields(self) -> Dict[str, OtherModel]: return self._dynamic_fields
方案2:使用模型验证器(Pydantic v2)
Pydantic v2推荐使用@model_validator替代root_validator,写法更简洁直观:
from pydantic import BaseModel, model_validator from typing import Dict, Any, Self class OtherModel(BaseModel): value: str count: int class ExpandedModel(BaseModel): fixed: int _dynamic: Dict[str, OtherModel] = {} @model_validator(mode="before") @classmethod def validate_all_fields(cls, data: Dict[str, Any]) -> Dict[str, Any]: # 处理固定字段 fixed = data.pop("fixed", None) if fixed is None: raise ValueError("'fixed'为必填字段") # 验证并转换动态字段 dynamic = {} for key, val in data.items(): dynamic[key] = OtherModel.model_validate(val) return {"fixed": fixed, "_dynamic": dynamic} # 支持字典式访问动态字段 def __getitem__(self, key: str) -> OtherModel: return self._dynamic[key] def __setitem__(self, key: str, value: OtherModel) -> None: self._dynamic[key] = value
方案3:组合式设计(更符合Pydantic规范)
如果可以接受输入数据的结构调整,将动态字段统一放到dynamic键下,无需验证器即可实现需求:
from pydantic import BaseModel from typing import Dict class OtherModel(BaseModel): value: str count: int class ExpandedModel(BaseModel): fixed: int dynamic: Dict[str, OtherModel] # 自定义属性访问,支持直接通过模型实例访问动态字段 def __getattr__(self, name: str) -> OtherModel: if name in self.dynamic: return self.dynamic[name] raise AttributeError(f"'ExpandedModel' object has no attribute '{name}'") # 支持字典式访问 def __getitem__(self, key: str) -> OtherModel: return self.dynamic[key]
这种方式更简洁,但需要输入数据为{"fixed": 123, "dynamic": {"key1": {"value": "test", "count": 1}}}的结构。如果需要接收扁平结构的输入,还是建议使用方案1或2。
内容的提问来源于stack exchange,提问作者CPHPython
相关产品推荐
相关产品推荐

