APIFlask入参为枚举值时,文档如何自动生成?

代码

from enum import Enum

from apiflask import APIFlask
from apiflask.fields import Integer
from apiflask.validators import OneOf

app = APIFlask(__name__, docs_ui='elements')


class APIFlaskEnum(Enum):
    @classmethod
    def choices(cls):
        return [i.value for i in cls]

    @classmethod
    def description(cls):
        return '\n\n'.join(f'{i.value}={i.name}' for i in cls)


class CategoryEnum(APIFlaskEnum):
    cat = 1
    dog = 2


@app.get('/say')
@app.input(dict(
    category=Integer(required=True, validate=OneOf(CategoryEnum.choices()))
), location='query')
@app.doc(description=CategoryEnum.description())
def say(data):
    """枚举值入参"""
    return CategoryEnum(data['category']).name


if __name__ == '__main__':
    app.run()

效果
image

以上为替代方案,但当参数有多个枚举类型时,该方案就不太适合了,有没有更优雅的方式解决呢?

还有一个是,要用两个\n才能显示出换行,有没有办法解决?

感谢!

优雅的方式可能是等 marshmallow 内置 Enum 支持的 PR 合并……

「当参数有多个枚举类型时」具体是指什么?没太明白。

要用两个\n才能显示出换行

description 可以用 Markdown,不过也都差不多。另外或许可以考虑把这个放到字段的 description 里面:

category = Integer(
    required = True,
    validate = OneOf(CategoryEnum.choices()),
    metadata = {'description': CategoryEnum.description()}
)
1 个赞

谢谢回复!希望 marshmallow 能早日添加这个 feature

理想的情况是,这样写就能生成参数为枚举值的文档:

@app.input(dict(
    category=Integer(required=True, validate=OneOf(CategoryEnum))
), location='query')

根据建议优化了一下

from enum import Enum

from apiflask import APIFlask
from apiflask.fields import Integer
from apiflask.validators import OneOf

app = APIFlask(__name__, docs_ui='elements')


class APIFlaskEnum(Enum):
    @classmethod
    def choices(cls):
        return [i.value for i in cls]

    @classmethod
    def description(cls):
        desc = '<br>'.join(f'{i.value}={i.name}' for i in cls)
        if cls.__doc__ and cls.__doc__ != 'An enumeration.':
            desc = cls.__doc__ + '<br>' + desc
        return desc


class CategoryEnum(APIFlaskEnum):
    """类别"""
    cat = 1
    dog = 2


@app.get('/say')
@app.input(dict(
    category=Integer(required=True, validate=OneOf(CategoryEnum.choices()),
                     metadata={'description': CategoryEnum.description()})
), location='query')
def say(data):
    """枚举值入参"""
    return CategoryEnum(data['category']).name


if __name__ == '__main__':
    app.run()

效果

image

1 个赞

好消息!marshmallow 3.18.0 已更新枚举字段 marshmallow.fields.Enum

安装

pip install -U marshmallow

示例代码

from enum import Enum

from marshmallow import Schema, fields


class CategoryEnum(Enum):
    cat = 1
    dog = 2


class Pet:
    def __init__(self, name, category):
        self.name = name
        self.category = category


class PetSchema(Schema):
    name = fields.String()
    category = fields.Enum(CategoryEnum)


class PetByValueSchema(Schema):
    name = fields.String()
    category = fields.Enum(CategoryEnum, by_value=True)


pet = Pet(name='Kitty', category=CategoryEnum.cat)
print(PetSchema().dump(pet))
print(PetByValueSchema().dump(pet))
# {'name': 'Kitty', 'category': 'cat'}
# {'name': 'Kitty', 'category': 1}

print(PetSchema().load({'name': 'Kitty', 'category': 'cat'}))
print(PetByValueSchema().load({'name': 'Kitty', 'category': 1}))
# {'name': 'Kitty', 'category': <CategoryEnum.cat: 1>}
# {'name': 'Kitty', 'category': <CategoryEnum.cat: 1>}

API 代码

import enum

from apiflask import APIFlask
from apiflask.fields import Integer
from marshmallow.fields import Enum


class CategoryEnum(enum.Enum):
    """类别"""
    cat = 1
    dog = 2


app = APIFlask(__name__, docs_ui='elements')


@app.get('/name')
@app.input(dict(
    category=Enum(CategoryEnum)
), location='query')
def name(data):
    return str(data['category'])


@app.get('/value')
@app.input(dict(
    category=Enum(CategoryEnum, by_value=Integer)
), location='query')
def value(data):
    return str(data['category'])


if __name__ == '__main__':
    app.run()
1 个赞

试了一下,apispec 目前还没法正确为 Enum 字段解析 OpenAPI spec,估计还要等后续支持。

apispec 已更新枚举字段解析 enum2properties()

安装

pip install apispec==6.0.0

代码

from enum import Enum

from apispec import APISpec
from marshmallow import fields
from apispec.ext.marshmallow import MarshmallowPlugin


class MyEnum(Enum):
    one = 1
    two = 2


field = fields.Enum(MyEnum)
ma_plugin = MarshmallowPlugin()
spec = APISpec(title='', version='', openapi_version='3.0.3', plugins=(ma_plugin,))
ret = ma_plugin.converter.field2property(field)
print(ret)
# {'type': 'string', 'enum': 'one, two'}
1 个赞

那是不是在 API 文档里就可以直接渲染出来了?

试了一下不能直接渲染出来,如果辉哥有空麻烦看看,感谢

动画1

安装最新的 apispec 即可

pip install apispec -U

代码

import enum

from apiflask import APIFlask
from apiflask.fields import Integer
from marshmallow.fields import Enum


class CategoryEnum(enum.Enum):
    """类别"""
    cat = 1
    dog = 2


app = APIFlask(__name__, docs_ui='elements')


@app.get('/name')
@app.input(dict(
    category=Enum(CategoryEnum)
), location='query')
def name(data):
    return str(data['category'])


@app.get('/value')
@app.input(dict(
    category=Enum(CategoryEnum, by_value=Integer)
), location='query')
def value(data):
    return str(data['category'])


if __name__ == '__main__':
    app.run()

效果

动画

有幸和题主经历了一样的问题,区别的是我的需求是在reponse中,经历了和题主一样的过程,很巧就看到这篇帖子,然后静静等待辉总1.2.0 的发布 :smiley:

我的看法:

  1. 不用等 APIFlask 更新,直接升级 apispec 即可使用。不过还是有缺点,枚举值没有注释信息。cat、dog还好理解,换成别的专业名词就难了。
  2. 希望辉哥能把 from marshmallow.fields import Enum as Enum 加进 apiflask\fields.py 里,这样子再导入 Enum 能少写一行。