Postponed annotations
Note
Both postponed annotations via the future import and ForwardRef
require python 3.7+.
Postponed annotations (as described in PEP563) "just work".
from __future__ import annotations from typing import List from pydantic import BaseModel class Model(BaseModel): a: List[int] print(Model(a=('1', 2, 3))) #> a=[1, 2, 3]
(This script is complete, it should run "as is")
Internally, pydantic will call a method similar to typing.get_type_hints
to resolve annotations.
In cases where the referenced type is not yet defined, ForwardRef
can be used (although referencing the
type directly or by its string is a simpler solution in the case of
self-referencing models).
In some cases, a ForwardRef
won't be able to be resolved during model creation.
For example, this happens whenever a model references itself as a field type.
When this happens, you'll need to call update_forward_refs
after the model has been created before it can be used:
from typing import ForwardRef from pydantic import BaseModel Foo = ForwardRef('Foo') class Foo(BaseModel): a: int = 123 b: Foo = None Foo.update_forward_refs() print(Foo()) #> a=123 b=None print(Foo(b={'a': '321'})) #> a=123 b=Foo(a=321, b=None)
(This script is complete, it should run "as is")
Warning
To resolve strings (type names) into annotations (types), pydantic needs a namespace dict in which to
perform the lookup. For this it uses module.__dict__
, just like get_type_hints
.
This means pydantic may not play well with types not defined in the global scope of a module.
For example, this works fine:
from __future__ import annotations from typing import List # <-- List is defined in the module's global scope from pydantic import BaseModel def this_works(): class Model(BaseModel): a: List[int] print(Model(a=(1, 2)))
While this will break:
from __future__ import annotations from pydantic import BaseModel def this_is_broken(): # List is defined inside the function so is not in the module's # global scope! from typing import List class Model(BaseModel): a: List[int] print(Model(a=(1, 2)))
Resolving this is beyond the call for pydantic: either remove the future import or declare the types globally.
Self-referencing Models🔗
Data structures with self-referencing models are also supported, provided the function
update_forward_refs()
is called once the model is created (you will be reminded
with a friendly error message if you forget).
Within the model, you can refer to the not-yet-constructed model using a string:
from pydantic import BaseModel class Foo(BaseModel): a: int = 123 #: The sibling of `Foo` is referenced by string sibling: 'Foo' = None Foo.update_forward_refs() print(Foo()) #> a=123 sibling=None print(Foo(sibling={'a': '321'})) #> a=123 sibling=Foo(a=321, sibling=None)
(This script is complete, it should run "as is")
Since python 3.7
, you can also refer it by its type, provided you import annotations
(see
above for support depending on Python
and pydantic versions).
from __future__ import annotations from pydantic import BaseModel class Foo(BaseModel): a: int = 123 #: The sibling of `Foo` is referenced directly by type sibling: Foo = None Foo.update_forward_refs() print(Foo()) #> a=123 sibling=None print(Foo(sibling={'a': '321'})) #> a=123 sibling=Foo(a=321, sibling=None)
(This script is complete, it should run "as is")