我可以覆盖 Pydantic 父模型中的字段以使其可选吗?

shi*_*asu 5 python pydantic fastapi

我有两个像这样的 pydantic 课程。

class Parent(BaseModel):
    id: int
    name: str
    email: str

class ParentUpdate(BaseModel):
    id: Optional[int]
    name: Optional[str]
    email: Optional[str]
Run Code Online (Sandbox Code Playgroud)

这两者实际上是相同的,但Parent该类要求所有字段。我想Parent在 FastAPI 中使用POST 请求正文的类,因此所有字段都应该是必需的。但我想将后者用于 PUT 请求正文,因为用户可以设置选择性字段而其余字段保持不变。我已经查看了必需的可选字段,但它们与我想要做的不符。

如果有一种方法可以继承Parent该类ParentUpdate并修改其中的所有字段ParentOptional减少混乱。此外,类中存在一些验证器Parent,我必须在ParentUpdate类中重写这些验证器,但我也想避免这些验证器。

有没有办法做到这一点?谢谢。

sha*_*all 19

您可以将子类中的可选字段设置为必填字段,但不能将子类中的必填字段设置为可选字段。在 fastapi 作者 tiangolo 的样板项目中,他在示例中使用了这样的模式:

class ParentBase(BaseModel):
    """Shared properties."""
    name: str
    email: str

class ParentCreate(ParentBase):
    """Properties to receive on item creation."""
    # dont need id here if your db autocreates it
    pass

class ParentUpdate(ParentBase):
    """Properties to receive on item update."""
    # dont need id as you are likely PUTing to /parents/{id}
    # other fields should not be optional in a PUT
    # maybe what you are wanting is a PATCH schema?
    pass

class ParentInDBBase(ParentBase):
    """Properties shared by models stored in DB - !exposed in create/update."""
    # primary key exists in db, but not in base/create/update
    id: int                             

class Parent(ParentInDBBase):
    """Properties to return to client."""
    # optionally include things like relationships returned to consumer
    # related_things: List[Thing]
    pass

class ParentInDB(ParentInDBBase):
    """Additional properties stored in DB."""
    # could be secure things like passwords?
    pass
Run Code Online (Sandbox Code Playgroud)

是的,我同意这非常冗长,但我希望不是这样。您仍然可能最终得到更特定于 UI 中特定表单的其他架构。显然,您可以删除其中一些,因为它们在本示例中不是必需的,但根据数据库中的其他字段,可能需要它们,或者您可能需要设置默认值、验证等。

根据我对验证器的经验,您必须重新声明它们,但您可以使用共享函数,即:

def clean_article_url(cls, v):
    return clean_context_url(v.strip())

class MyModel(BaseModel):
    article_url: str

    _clean_url = pydantic.validator("article_url", allow_reuse=True)(clean_article_url)
Run Code Online (Sandbox Code Playgroud)


ela*_*no7 10

覆盖字段是可能且容易的。(有人提到不可能将必填字段覆盖为可选字段,但我不同意)。

这个例子运行没有任何问题:

class Parent(BaseModel):
    id: int
    name: str
    email: str

class ParentUpdate(Parent): ## Note that this inherits 'Parent' class (not BaseModel)
    id: Optional[int]  # this will convert id from required to optional
Run Code Online (Sandbox Code Playgroud)


And*_*ous 6

正如在类似问题的回答中已经概述的那样,我正在使用以下方法(归功于 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

   
Run Code Online (Sandbox Code Playgroud)

在您的示例中,您将像这样使用它:

@optional
class ParentUpdate(Parent):
    pass
Run Code Online (Sandbox Code Playgroud)