如何扩展一个 pydantic 对象并更改某些字段的类型?

How to extend a pydantic object and change some fileds' type?

有两个类似的玩世不恭的对象。唯一的区别是一些字段是可选的。 我怎样才能只定义一个对象中的字段并扩展到另一个对象中?

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

你自己回答的差不多了。除非有更多问题。

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

另一种看待它的方法是将基定义为可选的,然后创建一个验证器来检查何时需要:

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

我通过 __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

或者像在这个线程中那样使用元类:

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