You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 06:06:45