Python pydantic,让祖先的每一个字段都是Optional

时间:2021-01-21 21:45:23

标签: python pydantic

我有两门课:

class UserCreate(BaseModel):
    avatar: HttpUrl = Field(..., description="Avatar", example="https://picsum.photos/200")
    name: str = Field(..., max_length=20, description="A single word", example='Ivan')
    birthdate: datetime_date = Field(..., description="Two digits", example='1980-1-1')
    comment: Optional[str] = Field(..., max_length=512, description="lorem ipsum about a user", example='blah blah')

并且我想创建一个 UserUpdate 类,该类将从父类继承每个字段并使其成为 Optional

Result 类必须如下所示:

class UserUpdate(BaseModel):
    avatar: typing.Optional[HttpUrl] = Field(..., description="Avatar", example="https://picsum.photos/200")
    name: typing.Optional[str] = Field(..., max_length=20, description="A single word", example='Ivan')
    birthdate: typing.Optional[datetime_date] = Field(..., description="Two digits", example='1980-1-1')
    comment: typing.Optional[str] = Field(..., max_length=512, description="lorem ipsum about a user", example='blah blah')

但显然,我想自动制作,例如:

class UserUpdate(UserCreate):
    def foo(fields_from_user_create):
        for fields in fields_from_user_create:
            field = typing.Optional(field)

2 个答案:

答案 0 :(得分:1)

我正在使用以下方法(归功于 Aron Podrigal):

import inspect   
from pydantic import BaseModel   


def optional(*fields):
    """Decorator function used to modify a pydantic model's fields to all be optional.
    Alternatively, you can  also pass the field names that should be made optional as arguments
    to the decorator.
    Taken from https://github.com/samuelcolvin/pydantic/issues/1223#issuecomment-775363074
    """   
    def dec(_cls):
        for field in fields:
            _cls.__fields__[field].required = False
        return _cls

    if fields and inspect.isclass(fields[0]) and issubclass(fields[0], BaseModel):
        cls = fields[0]
        fields = cls.__fields__
        return dec(cls)

    return dec

   

在您的示例中,您可以这样使用它:

@optional
class UserUpdate(UserCreate):
    pass

答案 1 :(得分:0)

直接来自PrettyWood的解决方案

<块引用>

这是一种方法

from copy import deepcopy
from typing import Optional, Type, TypeVar

from pydantic import BaseModel, create_model

BaseModelT = TypeVar('BaseModelT', bound=BaseModel)


def to_optional(model: Type[BaseModelT], name: Optional[str] = None) -> Type[BaseModelT]:
    """
    Create a new BaseModel with the exact same fields as `model`
    but making them all optional
    """
    field_definitions = {}

for name, field in model.__fields__.items():
    optional_field_info = deepcopy(field.field_info)
    # Do not change default value of fields that are already optional
    if optional_field_info.default is ...:
        optional_field_info.default = None
    field_type = model.__annotations__.get(name, field.outer_type_)
    field_definitions[name] = (field_type, optional_field_info)

return create_model(name or f'Optional{model.__name__}', **field_definitions)  # type: ignore[arg-type]
<块引用>

希望有帮助;)

Original answer