如何使用 BaseModel 在 FastAPI 的发布请求中添加未知参数?

问题描述 投票:0回答:3

我有以下代码:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Request(BaseModel):
    user_name: str
    age: int
    # other unknown arguments


@app.post("/home")
def write_home(request: Request):
    print(request.__dict__)
    return {
        "user_name": request.user_name,
        "age": request.age,
        # other arguments...
    }

我希望请求采用可选参数(如

height
weight
等),但这些参数可能是 unknown.

提前致谢

我试图在请求中直接添加它们,但它没有打印出其他未指定的参数

python fastapi pydantic optional-parameters
3个回答
0
投票

具有未知参数与 Pydantic(类型安全数据解析和验证)的意图完全相反。您可以做的(以及我会做的)是定义一个字段

extra
(或类似字段),用于保存动态额外数据: 从输入 import Any

class MyRequest(BaseModel):
    user_name: str
    age: int
    extra: dict[str, Any]

然后你知道哪些字段总是需要存在,任何未知的都放在

extra
字段中。


0
投票

简单的解决方案

我认为最简单的解决方案是使用extra = "allow"设置

configure
您的模型(默认设置为
extra = "ignore"
)。使用该设置,将任何额外的名称-值对传递给模型构造函数将使用提供的值和类型在该模型instance上动态创建字段。

举个例子:

from fastapi import FastAPI
from pydantic import BaseModel


app = FastAPI()


class Model(BaseModel):
    user_name: str
    age: int

    class Config:
        extra = "allow"


@app.post("/home")
def write_home(model: Model) -> Model:
    print(model)
    return model

现在您可以像这样发布任意附加数据,例如:

{
  "user_name": "string",
  "age": 0,
  "height": 3.14
}

print
语句的输出为
user_name='string' age=0 height=3.14
,响应正文与请求完全一致


潜在风险

我认为这里有一个大警告,它不是特定于 FastAPI,而是一般的 Pydantic 模型:

使用

extra = "allow"
设置,任何字段名称将可用。这可能会产生严重的意想不到的后果,因为提供的名称可以覆盖模型命名空间中的现有名称,包括内部属性(例如
__fields__
)和预定义方法(例如
dict
)的名称。

在 FastAPI 端点的上下文中,想象这样一种情况,某人

POST
像这样的有效负载:

{
  "user_name": "string",
  "age": 0,
  "dict": 1
}

直到需要调用该实例的

dict
方法为止,这都可以正常工作,这恰好是响应形成过程中的情况。

换句话说,我们的

print(model)
似乎可以正常工作,产生
user_name='string' age=0 dict=1
,但是尝试从我们的路由处理程序 return使用 TypeError: 'int' object is not callable
 使服务器崩溃

这只是一个例子,但这应该说明,如果你处理不当,为什么这可能是危险的或至少是有问题的。


其他注意事项

您还需要注意的一些小注意事项:

  • 这可能是显而易见的,但不会对任何这些额外字段值进行验证。通过配置的(或默认的)JSON 解码器解析后,它们将按原样分配给模型实例
  • OpenAPI 文档当然不能将这些字段显示为已接受的请求正文模式的一部分或包含在响应模型模式中。

0
投票

您可以使用

await request.json()
request
应该是 Starlette 的
Request
对象
的实例)将请求主体解析为 JSON,如here(参见选项 4)和 here(参见选项1).通过这种方式,您可以为
required
known 参数获得 BaseModel,同时仍然能够接受以前的 unknown 参数。

request.json()
将返回一个
dict
对象——如果您想知道如何循环遍历字典项,请参见here

例子

from fastapi import FastAPI, Request
from pydantic import BaseModel

app = FastAPI()


class Base(BaseModel):
    username: str
    age: int


@app.post('/')
async def main(base: Base, request: Request):
    return await request.json()

输入示例(您可以在

http://127.0.0.1:8000/docs
使用 Swagger UI autodocs 来测试端点):

{
  "username": "john",
  "gender": "m",
  "age": 20,
  "height": 1.95,
  "weight": 90
}

如果您根本不想使用 Pydantic

BaseModel
,您仍然可以使用
request.json()
将请求正文解析为 JSON,但是不会对您想要的必需/已知参数进行验证定义,除非您在端点内或在依赖类/函数中自行执行验证检查。如果您想这样做,请查看上面第一段中给出的链接答案,其中还演示了如何检查 JSON 对象的有效性并在客户端发送无效 JSON 时引发异常。在上面的示例中,此验证由 FastAPI 负责(由于使用了
Base
模型)。

© www.soinside.com 2019 - 2024. All rights reserved.