如何使用 FastAPI 在 OpenAPI/Swagger 中记录默认值 None/null?

How to document default None/null in OpenAPI/Swagger using FastAPI?

使用 ORM,我想做一个 POST 请求让一些字段具有 null 值,这些字段将在数据库中转换为那里指定的默认值。

问题是 OpenAPI (Swagger) docs,忽略了默认的 None 并且仍然默认提示一个 UUID

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
from uuid import UUID
import uvicorn


class Table(BaseModel):
    # ID: Optional[UUID]      # the docs show a example UUID, ok
    ID: Optional[UUID] = None # the docs still shows a uuid, when it should show a null or valid None value.

app = FastAPI()  
    
@app.post("/table/", response_model=Table)
def create_table(table: Table):
    # here we call to sqlalchey orm etc.
    return 'nothing important, the important thing is in the docs'
    
if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

docs 的 OpenAPI 模式示例(请求正文)中,我们发现:

{
 "ID": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}

这不行,因为我指定默认值为None,所以我希望这样:

{
 "ID": null, # null is the equivalent of None here
}

这会将 null 传递给 ID,最后将在数据库中解析为默认值(即新生成的 UUID)。

当您声明 Optional 参数时,用户不必为了成为 null 而在他们的请求中包含这些参数(用 null 值指定)。参数的默认值将为 null,除非用户在发送请求时指定其他值。

因此,您所要做的就是使用 Configschema_extra 为 Pydantic 模型声明自定义 example,如 documentation 和如下所示。下面的示例将在 OpenAPI (Swagger UI) 中创建一个空的(即 {})请求主体,可以成功提交(因为 ID 是模型的唯一属性并且是可选)。

class Table(BaseModel):
    ID: Optional[UUID] = None
    
    class Config:
        schema_extra = {
            "example": {
            }
        }

@app.post("/table/", response_model=Table)
def create_table(table: Table):
    return table

如果 Table 模型包含一些其他 必需的 属性,您可以为这些属性添加 example 值,如下所示:

class Table(BaseModel):
    ID: Optional[UUID] = None
    some_attr: str
    
    class Config:
        schema_extra = {
            "example": {
                "some_attr": "Foo"
            }
        }

如果您想保留auto-generated 示例 其余属性除了ID 属性的示例,您可以使用以下命令从生成的模式中的模型属性中删除 ID(受 Schema customization 启发):

class Table(BaseModel):
    ID: Optional[UUID] = None
    some_attr: str
    some_attr2: float
    some_attr3: bool
    
    class Config:
        @staticmethod
        def schema_extra(schema: Dict[str, Any], model: Type['Table']) -> None:
            del schema.get('properties')['ID']

此外,如果您想为某些属性添加自定义 example,您可以使用 Field()(如 here 所述);例如,some_attr: str = Field(example="Foo").

另一种可能的解决方案是修改生成的 OpenAPI 架构,如 的解决方案 3 中所述。不过,上述解决方案可能更适合这种情况。

备注

ID: Optional[UUID] = None 等同于 ID: UUID = None。正如之前在 FastAPI 网站中记录的那样(参见 ):

The Optional in Optional[str] is not used by FastAPI, but will allow your editor to give you better support and detect errors.

从那时起,FastAPI 修改了他们的 documentation 如下:

The Union in Union[str, None] will allow your editor to give you better support and detect errors.

因此,ID: Union[UUID, None] = None与上述两个选项相同。在Python 3.10及以上版本中,还可以使用:ID: UUID| None = None.

根据 FastAPI documentation(参见上述 link 中的 Info 部分):

Have in mind that the most important part to make a parameter optional is the part:

= None

or the:

= Query(default=None)

as it will use that None as the default value, and that way make the parameter not required.

The Union[str, None] part allows your editor to provide better support, but it is not what tells FastAPI that this parameter is not required.