我正在尝试自动将Pydantic模型转换为DB模式。为此,我将递归地遍历Pydantic模型的字段,以确定字段的类型。
作为一个例子,我有这个简单的模型:
from typing import List
from pydantic import BaseModel
class TestModel(BaseModel):
tags: List[str]
我使用__fields__
属性递归地遍历模型,如下所述:https://docs.pydantic.dev/usage/models/#model-properties
如果我做type(TestModel).__fields__['tags']
,我看到:
ModelField(name='tags', type=List[str], required=True)
我想以编程方式检查ModelField
类型是否具有List
起源。我尝试了以下方法,但都不起作用:
type(TestModel).__fields__['tags'].type_ is List[str]
type(TestModel).__fields__['tags'].type_ == List[str]
typing.get_origin(type(TestModel).__fields__['tags'].type_) is List
typing.get_origin(type(TestModel).__fields__['tags'].type_) == List
令人沮丧的是,这确实返回True
:
type(TestModel).__fields__['tags'].type_ is str
我确认字段是List
类型的正确方法是什么?
Pydantic有形状的概念田地的。这些形状被编码为整数,并在fields
模块中作为常量可用。这里已经包含了或多或少的标准类型。如果一个字段用list[T]
注释,那么该字段的shape
属性将为SHAPE_LIST
type_
为T
。
type_
指向元素类型在所有不是SHAPE_SINGLETON
的上下文中,即容器类型。这就是为什么你在你的例子中得到str
。
因此,对于像list
这样简单的东西,您可以简单地根据该常量检查形状:
from pydantic import BaseModel
from pydantic.fields import SHAPE_LIST
class TestModel(BaseModel):
tags: list[str]
other: tuple[str]
tags_field = TestModel.__fields__["tags"]
other_field = TestModel.__fields__["other"]
assert tags_field.shape == SHAPE_LIST
assert other_field.shape != SHAPE_LIST
如果您想更深入地了解实际的注释字段的值,存储在字段的annotation
属性中。有了它,你应该能够做所有typing
相关的分析,如get_origin
。
这意味着完成检查的另一种方式是:
from typing import get_origin
from pydantic import BaseModel
class TestModel(BaseModel):
tags: list[str]
other: tuple[str]
tags_field = TestModel.__fields__["tags"]
other_field = TestModel.__fields__["other"]
assert get_origin(tags_field.annotation) is list
assert get_origin(other_field.annotation) is tuple
遗憾的是,据我所知,这两个属性在任何地方都没有官方文档,但是开源的美妙之处在于我们可以自己检查。属性和形状常量都没有以任何通常的方式被混淆、保护或私有,所以我假设它们是稳定的(至少在Pydantic v2下降之前)。
丹尼尔的回答是更pydantic,回答你的OP,只是得到.annotation
attr(或属性-方法?)在字段。
一样,
>>> class Foo(BaseModel):
... x: Optional[dict[str,list[int]]]
...
>>> Foo.__fields__
... typing.Optional[dict[str, list[int]]]
然后通过访问注释的内部下标__args__