首页 > 解决方案 > Python pydantic,使祖先的每个字段都是可选的

问题描述

我有 2 节课:

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)

标签: pythonpydantic

解决方案


直接来自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]

希望能帮助到你 ;)

原始答案


推荐阅读