python-3.x 如何扩展一个Pydantic对象并改变一些字段的类型?

kgsdhlau  于 2022-12-24  发布在  Python
关注(0)|答案(3)|浏览(188)

有两个类似的pydantic对象。唯一的区别是一些字段是可选的。我如何在一个对象中定义字段并扩展到另一个对象中?

class ProjectCreateObject(BaseModel):
    project_id: str
    project_name: str
    project_type: ProjectTypeEnum
    depot: str
    system: str
    ...

class ProjectPatchObject(ProjectCreateObject):
    project_id: str
    project_name: Optional[str]
    project_type: Optional[ProjectTypeEnum]
    depot: Optional[str]
    system: Optional[str]
    ...
qlckcl4x

qlckcl4x1#

我找到了一个很好的简单的方法,通过__init__subclass__。文档也可以成功地生成。

class ProjectCreateObject(BaseModel):
    project_id: str
    project_name: str
    project_type: ProjectTypeEnum
    depot: str
    system: str
    ...

    def __init_subclass__(cls, optional_fields=None, **kwargs):
        """
        allow some fields of subclass turn into optional
        """
        super().__init_subclass__(**kwargs)
        if optional_fields:
            for field in optional_fields:
                cls.__fields__[field].outer_type_ = Optional
                cls.__fields__[field].required = False

_patch_fields = ProjectCreateObject.__fields__.keys() - {'project_id'}

class ProjectPatchObject(ProjectCreateObject, optional_fields=_patch_fields):
    pass
ljsrvy3e

ljsrvy3e2#

你自己差不多已经回答了。除非还有别的问题。

from typing import Optional
from pydantic import BaseModel

class ProjectCreateObject(BaseModel):
    project_id: str
    project_name: str
    project_type: str
    depot: str
    system: str

class ProjectPatchObject(ProjectCreateObject):
    project_name: Optional[str]
    project_type: Optional[str]
    depot: Optional[str]
    system: Optional[str]

if __name__ == "__main__":
    p = ProjectCreateObject(
        project_id="id",
        project_name="name",
        project_type="type",
        depot="depot",
        system="system",
    )
    print(p)

    c = ProjectPatchObject(project_id="id", depot="newdepot")
    print(c)

运行此命令可得到:

project_id='id' project_name='name' project_type='type' depot='depot' system='system'
project_id='id' project_name=None project_type=None depot='newdepot' system=None

另一种方法是将base定义为可选的,然后创建一个验证器来检查是否需要:

from pydantic import BaseModel, root_validator, MissingError

class ProjectPatchObject(BaseModel):
    project_id: str
    project_name: Optional[str]
    project_type: Optional[str]
    depot: Optional[str]
    system: Optional[str]

class ProjectCreateObject(ProjectPatchObject):
    @root_validator
    def check(cls, values):
        for k, v in values.items():
            if v is None:
                raise MissingError()
        return values
b91juud3

b91juud33#

或者像下面这样使用元类:Make every fields as optional with Pydantic

class AllOptional(pydantic.main.ModelMetaclass):
    def __new__(self, name, bases, namespaces, **kwargs):
        annotations = namespaces.get('__annotations__', {})
        for base in bases:
            annotations.update(base.__annotations__)
        for field in annotations:
            if not field.startswith('__') and field != 'project_id':
                annotations[field] = Optional[annotations[field]]
        namespaces['__annotations__'] = annotations
        return super().__new__(self, name, bases, namespaces, **kwargs)

在你的例子中...

class ProjectPatchObject(ProjectCreateObject, metaclass=AllOptional):
    ...

相关问题