pydantic 的 update_forward_refs 引发输入 NameError

ish*_*efi 7 python typing python-3.x python-typing pydantic

Python 3.9 - 我有以下模块:

from __future__ import annotations
from typing import TYPE_CHECKING
from pydantic import BaseModel

if TYPE_CHECKING:
    from typing import Optional

class A(BaseModel):
    id: int
    class Config:
        orm_mode = True

class B(A):
    foo: C

class C(A):
    bar: Optional[str]

C.update_forward_refs()

c = C(id=1, bar='bar')
b = B(id=2, foo=c)
Run Code Online (Sandbox Code Playgroud)

当我导入这个模块时,它会引发NameError: name 'Optional' is not defined. 我可以删除该if TYPE_CHECKING部分,但我知道这是最佳实践(例如,如果我使用自己的类型,则可以防止循环导入)。当我删除B.update_forward_refs()呼叫时,它会引发pydantic.errors.ConfigError: field "foo" not yet prepared so type is still a ForwardRef, you might need to call B.update_forward_refs().

知道如何克服这个问题吗?

mik*_*sus 6

正如您所说,TYPE_CHECKING您的示例中不需要,但对于循环导入,您可能需要它。我将举一个更简单的例子来说明如何解决循环导入问题。

我们有以下模块:

foo.py

from typing import TYPE_CHECKING
from pydantic import BaseModel

if TYPE_CHECKING:
    from bar import Bar

class Foo(BaseModel):
    reference: 'Bar'
Run Code Online (Sandbox Code Playgroud)

酒吧.py

from typing import TYPE_CHECKING, Optional
from pydantic import BaseModel

if TYPE_CHECKING:
    from foo import Foo

class Bar(BaseModel):
    reference: Optional['Foo']
Run Code Online (Sandbox Code Playgroud)

主要.py

from bar import Bar
from foo import Foo

Bar.update_forward_refs(Foo=Foo)
Foo.update_forward_refs(Bar=Bar)

# Put Bar into Foo and Foo into Bar
Bar(reference=Foo(reference=Bar()))
Run Code Online (Sandbox Code Playgroud)

该解决方案有效(如果我们运行该main.py文件)并且不存在循环导入问题。了解我们如何将FooandBar作为关键字参数传递给update_forward_refs. 这是因为该方法默认使用模型模块的局部变量定义类型,而这些模块空间没有局部变量(正如我们使用的那样if TYPE_CHECKING:)。因此我们自己通过了这些。

如果您正在构建一个包,您可能需要update_forward_refs__init__.py文件中。