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))) #> Model 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).
You may need to call Model.update_forward_refs()
after creating the model,
this is because in the example below Foo
doesn't exist before it has been created (obviously) so ForwardRef
can't initially be resolved. You have to wait until after Foo
is created, then call update_forward_refs
to properly set types before the model 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()) #> Foo a=123 b=None print(Foo(b={'a': '321'})) #> Foo 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 dict to lookup,
for this it uses module.__dict__
just as get_type_hints
does. That means pydantic does 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(): from typing import List # <-- List is defined inside the function so is not in the module's global scope 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 don't).
Within the model, you can refer to the not-yet-constructed model by 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()) #> Foo a=123 sibling=None print(Foo(sibling={'a': '321'})) #> Foo 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()) #> Foo a=123 sibling=None print(Foo(sibling={'a': '321'})) #> Foo a=123 sibling=<Foo a=321 sibling=None>
(This script is complete, it should run "as is")