如何解决我可以覆盖 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 举报,一经查实,本站将立刻删除。