I want to create a method on a base Pydantic model to instantiate child models with dummy data.
from __future__ import annotations
from pydantic import BaseModel
class BaseModelWrapper(BaseModel):
@classmethod
def make_dummy(cls) -> BaseModelWrapper:
for name, field in cls.model_fields.items():
if not field.is_required():
continue
# How can I create values based on the field type?
print(field.annotation)
return cls()
class XXX(BaseModelWrapper):
a: int | None
b: str
c: int
d: int | None = None
e: list[str]
# These should be equivalent
XXX.make_dummy()
XXX(a=None, b="", c=0, e=[])
The part I'm struggling with is how to programmatically map type annotations to values.
Let's say field.annotation
is int | None
. I could just create a dictionary to map that to None
, but there are tons of possible combinations of types, so this doesn't scale. There must be a cleaner way to create a value for each field.
I ultimately used @kamilcuk's answer to create the following method
from __future__ import annotations
from pydantic import BaseModel
from types import UnionType
from typing import Any, Union, get_args, get_origin
def is_union_type(t: type | UnionType) -> bool:
return get_origin(t) in [Union, UnionType]
class BaseModelWrapper(BaseModel):
@classmethod
def make_dummy(cls) -> Self:
kwargs = {}
for name, field in cls.model_fields.items():
if not field.is_required():
continue
if field.annotation is None:
kwargs[name] = None
continue
t = field.annotation
if is_union_type(t):
types: tuple[type[Any]] = get_args(t)
if type(None) in types:
kwargs[name] = None
continue
t = types[0]
try:
# Will error if given `list[str]` / `dict[str]`
if issubclass(t, BaseValidator):
kwargs[name] = t.make_dummy()
else:
kwargs[name] = t()
except TypeError:
kwargs[name] = t()
return cls(**kwargs)