我正在使用 FastAPI 和 Pydantic 制作 API。
我想要一些 PATCH 端点,可以一次编辑记录的 1 或 N 个字段。 此外,我希望客户端只传递有效负载中的必要字段。
例子:
class Item(BaseModel):
name: str
description: str
price: float
tax: float
@app.post("/items", response_model=Item)
async def post_item(item: Item):
...
@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
...
在这个例子中,对于 POST 请求,我希望每个字段都是必需的。但是,在 PATCH 端点中,我不介意有效负载是否仅包含例如描述字段。这就是为什么我希望所有字段都是可选的。幼稚的方法:
class UpdateItem(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
price: Optional[float] = None
tax: Optional[float]
但这在代码重复方面会很糟糕。还有更好的选择吗?
最佳答案
元类解决方案
我刚刚想出了以下几点:
class AllOptional(pydantic.main.ModelMetaclass):
def __new__(self, name, bases, namespaces, **kwargs):
annotations = namespaces.get('__annotations__', {})
for base in bases:
annotations = {**annotations, **base.__annotations__}
for field in annotations:
if not field.startswith('__'):
annotations[field] = Optional[annotations[field]]
namespaces['__annotations__'] = annotations
return super().__new__(self, name, bases, namespaces, **kwargs)
将其用作:class UpdatedItem(Item, metaclass=AllOptional):
pass
所以基本上它用 Optional
替换所有非可选字段欢迎任何编辑!
以你的例子:
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
import pydantic
app = FastAPI()
class Item(BaseModel):
name: str
description: str
price: float
tax: float
class AllOptional(pydantic.main.ModelMetaclass):
def __new__(self, name, bases, namespaces, **kwargs):
annotations = namespaces.get('__annotations__', {})
print(bases)
print(bases[0].__annotations__)
for base in bases:
annotations = {**annotations, **base.__annotations__}
for field in annotations:
if not field.startswith('__'):
annotations[field] = Optional[annotations[field]]
namespaces['__annotations__'] = annotations
return super().__new__(self, name, bases, namespaces, **kwargs)
class UpdatedItem(Item, metaclass=AllOptional):
pass
# This continues to work correctly
@app.get("/items/{item_id}", response_model=Item)
async def get_item(item_id: int):
return {
'name': 'Uzbek Palov',
'description': 'Palov is my traditional meal',
'price': 15.0,
'tax': 0.5,
}
@app.patch("/items/{item_id}") # not using response_model=Item
async def update_item(item_id: str, item: UpdatedItem):
return item
关于python - 使用 Pydantic 将每个字段设为可选,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67699451/