python - 如何使用 Type 使 pydantic Field 接受子类?
问题描述
我试图让一个 Pydantic 模型中的一个字段接受我单独定义的一组 BaseModel 派生类或子类中的任何一个。阅读这里的文档,我天真地做了下面的事情,但失败了;然后我意识到我误读了文档,并且在这种情况下“一个字段可能只接受类(而不是实例)”,而且该示例中的 Foo 和 Bar 不是从 BaseModel 本身派生的(这很重要吗?) .
我猜我只是从一开始就误解了这一点,所以我的问题是:有没有正确的方法来做我想做的事情,而不是在子类上使用 Union,或者其他更好的方法?
额外的问题:只能接受类而不接受实例的常见用例是什么?
MRE:
from pydantic import BaseModel
from typing import Type, Union
class Foo(BaseModel):
pass
class Bar(Foo):
pass
class Baz(Foo):
pass
class Container(BaseModel):
some_foo: Type[Foo] # this fails
# this will run successfully --> some_foo: Union[Bar, Baz]
b = Baz()
c = Container(some_foo = b)
# Traceback (most recent call last):
# File "mre.py", line 20, in <module>
# c = Container(some_foo = b)
# File "pydantic/main.py", line 400, in pydantic.main.BaseModel.__init__
# pydantic.error_wrappers.ValidationError: 1 validation error for Container
# some_foo
# subclass of Foo expected (type=type_error.subclass; expected_class=Foo)
解决方案
这比看起来更棘手。
这是一个使用pydantic
's验证器的解决方案,但也许有一个更“pydantic”的方法。
from pydantic import BaseModel, validator
from typing import Any
class Foo(BaseModel):
pass
class Bar(Foo):
pass
class Baz(Foo):
pass
class NotFoo(BaseModel):
pass
class Container(BaseModel):
some_foo: Any
@validator("some_foo")
def validate_some_foo(cls, val):
if issubclass(type(val), Foo):
return val
raise TypeError("Wrong type for 'some_foo', must be subclass of Foo")
b = Bar()
c = Container(some_foo=b)
# Container(some_foo=Bar())
n = NotFoo()
c = container(some_foo=n)
# Traceback (most recent call last):
# File "/path/to/file.py", line 64, in <module>
# c = Container(some_foo=n)
# File "pydantic/main.py", line 400, in pydantic.main.BaseModel.__init__
# pydantic.error_wrappers.ValidationError: 1 validation error for Container
# some_foo
# Wrong type for 'some_foo', must be subclass of Foo (type=type_error)
请注意,自定义验证器必须引发 aValueError
或TypeError
(或其子类)pydantic
才能正确地重新引发ValudationError
.
您可能希望将特定类(而不是该类的实例)作为字段类型的一个原因是当您想使用该字段在以后使用该字段来实例化某些内容时。
这是一个例子:
from pydantic import BaseModel
from typing import Optional, Type
class Foo(BaseModel):
# x is NOT optional
x: int
class Bar(Foo):
y: Optional[str]
class Baz(Foo):
z: Optional[bool]
class NotFoo(BaseModel):
# a is NOT optional
a: str
class ContainerForClass(BaseModel):
some_foo_class: Type[Foo]
c = ContainerForClass(some_foo_class=Bar)
# At this point you know that you will use this class for something else
# and that x must be always provided and it must be an int:
d = c.some_foo_class(x=5, y="some string")
# Baz(x=5, z=None)
c = ContainerForClass(some_foo_class=Baz)
# Same here with x:
e = c.some_foo_class(x=6, z=True)
# Baz(x=6, z=True)
# Would't work with this:
c = ContainerForClass(some_foo_class=NotFoo)
# Traceback (most recent call last):
# File "/path/to/file.py", line 98, in <module>
# c = ContainerForClass(some_foo_class=NotFoo)
# File "pydantic/main.py", line 400, in pydantic.main.BaseModel.__init__
# pydantic.error_wrappers.ValidationError: 1 validation error for ContainerForClass
# some_foo_class
# subclass of Foo expected (type=type_error.subclass; expected_class=Foo
推荐阅读
- arrays - 如何在 Symfony 中使用 foreach 循环从对象中获取数据?
- java - 递归地修改使用给定注释注释的对象中的所有字段
- java - 如何关联Android Studio中RelativeLayout的两个视图?
- tortoisehg - 在修订历史视图中包含“工作目录”伪修订
- r - 从邻域名称中获取纬度/经度
- android - 有没有办法直接从 GLSurfaceView(OpenGL ES) 创建视频文件?
- heroku - heroku 部署的应用程序不安全
- java - Java中两个日期之间的周数
- python - 在列表列表中逐项操作
- c++ - 用于 std::list 的 C++11 基于范围的 for 循环