簡體   English   中英

如何使用 Type 使 pydantic Field 接受子類?

[英]How to make a pydantic Field accept subclasses using Type?

我試圖在一個 Pydantic model 中有一個字段接受我單獨定義的一組 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驗證器工作的解決方案,但也許有一種更“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)

請注意,自定義驗證器必須引發ValueErrorTypeError (或其子類),以便pydantic能夠正確地重新引發ValudationError

您可能希望將特定的 class(而不是該類的實例)作為字段類型的一個原因是當您想使用該字段在以后使用該字段實例化某些內容時。

這是一個例子:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM