python Pydantic:从Dict[str,OtherModel]创建具有固定和扩展字段的模型,Typescript [key:字符串]方式

a1o7rhls  于 2023-02-21  发布在  Python
关注(0)|答案(1)|浏览(134)

similar question开始,我们的目标是创建一个类似于以下Typescript界面的模型:

interface ExpandedModel {
  fixed: number;
  [key: string]: OtherModel;
}

但是,OtherModel需要验证,因此只需使用:

class ExpandedModel(BaseModel):
    fixed: int

    class Config:
        extra = "allow"

还不够。我试过root(pydantic docs):

class VariableKeysModel(BaseModel):
    __root__: Dict[str, OtherModel]

但是做一些像这样的事情:

class ExpandedModel(VariableKeysModel):
    fixed: int

不可能,原因如下:
ValueError:root不能与其他字段混合
@root_validator (example from another answer)之类的东西在这种情况下有用吗?

qncylg1j

qncylg1j1#

谢天谢地,Python不是TypeScript,正如注解here中提到的,对象通常不是字典,动态属性在几乎所有情况下都被认为是错误的形式。
当然,您仍然可以动态地设置属性,但是它们将永远不会被静态类型检查器(如Mypy或IDE)识别。这意味着您将不会获得这些动态字段的自动建议。只有在类的命名空间中静态定义的属性才被视为该类的成员。
也就是说,您可以滥用extra配置选项,允许将任意字段动态添加到模型中,同时通过root_validator将所有对应的值强制为特定类型。

from typing import Any

from pydantic import BaseModel, root_validator

class Foo(BaseModel):
    a: int

class Bar(BaseModel):
    b: str

    @root_validator
    def validate_foo(cls, values: dict[str, Any]) -> dict[str, Any]:
        for name, value in values.items():
            if name in cls.__fields__:
                continue  # ignore statically defined fields here
            values[name] = Foo.parse_obj(value)
        return values

    class Config:
        extra = "allow"

演示:

if __name__ == "__main__":
    from pydantic import ValidationError

    bar = Bar.parse_obj({
        "b": "xyz",
        "foo1": {"a": 1},
        "foo2": Foo(a=2),
    })
    print(bar.json(indent=4))

    try:
        Bar.parse_obj({
            "b": "xyz",
            "foo": {"a": "string"},
        })
    except ValidationError as err:
        print(err.json(indent=4))

    try:
        Bar.parse_obj({
            "b": "xyz",
            "foo": {"not_a_foo_field": 1},
        })
    except ValidationError as err:
        print(err.json(indent=4))

输出:
一个一个二个一个一个一个三个一个一个一个一个一个四个一个
IMO的一个 * 更好 * 的方法是将动态的名称-对象对放入字典中,例如,您可以在Bar模型上定义一个单独的字段foos: dict[str, Foo],并通过这种方式获得自动验证。
或者,您可以针对特定情况完全抛弃外部基础模型,而将数据作为具有Foo值的原生字典来处理,并通过Foo模型解析所有这些值。

相关问题