使用PYDANIC使每个字段都是可选的 [英] Make every fields as optional with Pydantic

查看:0
本文介绍了使用PYDANIC使每个字段都是可选的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用FastAPI和Pydtic制作一个API。

我希望有一些补丁端点,其中一条记录的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请求,我希望每个字段都是必填的。然而,在补丁端点中,我并不介意有效负载是否只包含描述字段。这就是我希望所有字段都为可选的原因。

幼稚的做法:

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.update(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__', {})
        for base in bases:
            annotations.update(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

这篇关于使用PYDANIC使每个字段都是可选的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆