最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Creating a dummy Pydantic model generator - Stack Overflow

programmeradmin2浏览0评论

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 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.

Share Improve this question asked Mar 14 at 15:47 MattMatt 1,7135 gold badges31 silver badges62 bronze badges 2
  • If you a re open to using faker module then you can try this approaches: stackoverflow/questions/76993621/… – mx0 Commented Mar 14 at 22:07
  • The answers in that question require manually defining each field on each child model, whereas the solution I'm attempting here would automatically handle all child and their fields without manually defining them – Matt Commented Mar 18 at 3:20
Add a comment  | 

2 Answers 2

Reset to default 1

You can map them yourself.

class BaseModelWrapper(BaseModel):
    @classmethod
    def make_dummy(cls) -> BaseModelWrapper:
        kwargs = {}
        defaults = {
            int: int(),
            str: str(),
            list[str]: list(),
            int | None: None
        }
        for name, field in cls.model_fields.items():
            if not field.is_required():
                continue
            kwargs[name] = defaults[field.annotation]
        return cls(**kwargs)

You could something smart, like list() if isinstance(field.annotation, list) else None if field_is_optional(cls, name) else ... where "is_optional" comes from https://stackoverflow/a/76397722/9072753 .

You can really just call the type and create, so you could do something like special handling for unions and optionals and have it auto-constructed:

def type_is_optional(t: type):
    origin = get_origin(t)
    #print(field_name, ":", field_type, origin)
    if origin is Union:
        return type(None) in get_args(t)
    if origin is UnionType:
        return type(None) in get_args(t)
    return False

...

            # How can I create values based on the field type?
            kwargs[name] = (
                None
                if type_is_optional(field.annotation)
                else get_args(field.annotation)[0]()
                if get_origin(field.annotation) in (Union, UnionType)
                else field.annotation()
            )

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)
发布评论

评论列表(0)

  1. 暂无评论