使用FastAPI,我已经设置了一个POST端点,它接受一个命令,我希望这个命令不区分大小写,同时仍然有建议的值(即在SwaggerUI文档中)

为此,我设置了一个具有Command类的端点作为POST正文参数的模式:

@router.post("/command", status_code=HTTPStatus.ACCEPTED)  # @router is a fully set up APIRouter()
async def control_battery(command: Command):
    result = do_work(command.action)
    return result

对于Command我目前有2个可能的版本,这两个都没有我想要的全部功能.

from fastapi import HTTPException
from pydantic import BaseModel, field_validator
from typing import Literal

## VERSION 1
class Command(BaseModel):
    action: Literal["jump", "walk", "sleep"]


## VERSION 2
class Command(BaseModel):
    action: str

    @field_validator('action')
    @classmethod
    def validate_command(cls, v: str) -> str:
        """
        Checks if command is valid and converts it to lower.
        """
        if v.lower() not in {'jump', 'walk', 'sleep'}:
            raise HTTPException(status_code=422, detail="Action must be either 'jump', 'walk', or 'sleep'")
        return v.lower()

版本1的HAS显然不区分大小写,但具有正确的"建议值"行为,如下所示.

而版本2具有正确的大小写敏感性,并允许对验证进行更好的控制,但不再与架构的用户共享建议值.例如,在上面的图像中,"JUMP"将替换为"STRING".

如何将这两种方法的功能结合起来?

推荐答案

将类型指定为文字或某个字符串,并保持验证:

class Command(BaseModel):
    action: Literal["jump", "walk", "sleep"] | str

    @field_validator('action')
    @classmethod
    def validate_command(cls, v: str) -> str:
        """
        Checks if command is valid and converts it to lower.
        """
        if v.lower() not in {'jump', 'walk', 'sleep'}:
            raise HTTPException(status_code=422, detail="Action must be either 'jump', 'walk', or 'sleep'")
        return v.lower()

纯粹主义者可能会说Literal["jump", "walk", "sleep"] | str等同于str,但在这种情况下,人和计算机都可以推断"跳"和其他两个值是特殊输入.它的作用是提供示例值.

您可能不想重复文字值--传递mypy的解决方案是将它们设置为枚举:

from enum import Enum

class Action(Enum):
    jump = "jump"
    walk = "walk"
    sleep = "sleep"


class Command(BaseModel):
    action: Action | str

    @field_validator('action')
    @classmethod
    def validate_command(cls, v: str) -> str:
        """
        Checks if command is valid and converts it to lower.
        """
        if v.lower() not in Action.__members__:
            raise HTTPException(status_code=422, detail="Action must be either 'jump', 'walk', or 'sleep'")
        return v.lower()

另一种解决方案是继续使用Literal而不是枚举,并使用get_args来提取文本类型的值,如in this answer所述

Python相关问答推荐

对Numpy函数进行载体化

仿制药的类型铸造

连接两个具有不同标题的收件箱

如何使用Python将工作表从一个Excel工作簿复制粘贴到另一个工作簿?

如何过滤包含2个指定子字符串的收件箱列名?

在Mac上安装ipython

Python中绕y轴曲线的旋转

如何在给定的条件下使numpy数组的计算速度最快?

isinstance()在使用dill.dump和dill.load后,对列表中包含的对象失败

与命令行相比,相同的Python代码在Companyter Notebook中运行速度慢20倍

在方法中设置属性值时,如何处理语句不可达[Unreacable]";的问题?

OpenGL仅渲染第二个三角形,第一个三角形不可见

在用于Python的Bokeh包中设置按钮的样式

根据客户端是否正在传输响应来更改基于Flask的API的行为

统计numpy. ndarray中的项目列表出现次数的最快方法

如何获取包含`try`外部堆栈的`__traceback__`属性的异常

将相应的值从第2列合并到第1列(Pandas )

某些值的数值幂和**之间的差异

多个布尔条件的`jax.lax.cond`等效项

Python键盘模块不会立即检测到按键