pydantic's update_forward_refs raises typing NameError

Viewed 1326

Python 3.9 - I have the following module:

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)

When I import this module it raises NameError: name 'Optional' is not defined. I can remove the if TYPE_CHECKING part, but I understand that this is the best practice (to prevent circular imports if I use my own types for example). When I remove the B.update_forward_refs() call it raises pydantic.errors.ConfigError: field "foo" not yet prepared so type is still a ForwardRef, you might need to call B.update_forward_refs().

Any idea how to overcome this?

1 Answers

As you stated, the TYPE_CHECKING is not needed in your example but for circular imports, you might need that one. I'll give a bit simpler example to illustrate how to solve the circular import problem.

We have the following modules:

foo.py

from typing import TYPE_CHECKING
from pydantic import BaseModel

if TYPE_CHECKING:
    from bar import Bar

class Foo(BaseModel):
    reference: 'Bar'

bar.py

from typing import TYPE_CHECKING, Optional
from pydantic import BaseModel

if TYPE_CHECKING:
    from foo import Foo

class Bar(BaseModel):
    reference: Optional['Foo']

main.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()))

This solution works (if we run the main.py file) and does not have the circular import problem. See how we passed Foo and Bar as keyword arguments to the update_forward_refs. This is because the method defines the types using the locals of the models' modules by default and those module spaces do not have those as locals (as we used the if TYPE_CHECKING:). Therefore we passed these ourselves.

If you are building a package, you might want to update_forward_refs in the __init__.py files.

Related