微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

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

我有两个像这样的 pydantic 类。

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

class ParentUpdate(BaseModel):
    id: Optional[int]
    name: Optional[str]
    email: Optional[str]

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

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

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

解决方法

您可以将子类中的可选字段设为必需,但不能将子类中的必填字段设为可选。在 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

是的,我同意这是非常冗长的,我希望它不是。您仍然可能会在 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)
,

我提前道歉,我确信这是一个可怕的解决方法,但它对我有用:

def make_child_fields_optional(parent_class: Type[BaseModel],child_class: Type[BaseModel]):
    for key in parent_class.__fields__:
        child_class.__fields__.get(key).required = False
class BasePerson(BaseModel):
    name: str
    email: str
    login: str
class UpdatePerson(BasePerson):
    pass  # or whatever

make_child_fields_optional(BasePerson,UpdatePerson)
,

正如在对类似问题的回答中所述,我正在使用以下方法(归功于 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 ParentUpdate(Parent):
    pass
,

我的建议是不要发明困难的模式,我也对 pydantic 功能感兴趣,但所有这些功能看起来都非常丑陋且难以理解(甚至不适用于某些任务并且有限制)。 见Python pydantic,make every field of ancestor are Optional 来自pydantic维护者的回答

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。