You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在FastAPI文档中动态生成非Enum类型的下拉选项?

问题描述

我之前按照FastAPI官方文档的方法,用Enum类型实现了自动生成文档页面中的下拉选项,代码如下:

main.py 代码

from enum import Enum
from db import engine,SessionLocal
from models import Example,ExampleModel,Base,Color
from fastapi import FastAPI,Depends
from sqlalchemy.orm import Session
from sqlalchemy import select

app = FastAPI()

@app.on_event("startup")
def on_startup():
    Base.metadata.create_all(bind=engine)

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/colors",response_model=list[ExampleModel])
def read_colors(color: Color ,db: Session = Depends(get_db)):
    return db.execute(select(Example).where(Example.colors == color)).scalars().all()

models.py 代码

from sqlalchemy.orm import Mapped,mapped_column,DeclarativeBase
from sqlalchemy import Identity,Enum
from pydantic import BaseModel, ConfigDict
import enum

class Base(DeclarativeBase):
    pass

class Color(str,enum.Enum):
    RED = 'RED'
    BLUE = 'BLUE'

class Example(Base):
    __tablename__='example'
    id: Mapped[int] = mapped_column(Identity(always=True),primary_key=True)
    colors: Mapped[Color] = mapped_column(Enum(Color))

class ExampleModel(BaseModel):
    model_config = ConfigDict(from_attributes=True)
    id: int
    colors: Color

这样实现后,Color类型能在文档页面正常渲染为下拉选项。但现在数据库表结构改成了下面这样,colors变成了文本字段,里面有10-15个不同值,而且这些值会随时间动态变化:

class Example(Base):
    __tablename__='example'
    id: Mapped[int] = mapped_column(Identity(always=True),primary_key=True)
    colors: Mapped[str] 

我想知道这种场景下能不能动态生成colors的下拉选项?


解决方案

完全可以实现动态生成下拉选项,以下是两种实用方案:

方案一:动态生成Pydantic Enum(适合值更新不频繁的场景)

在应用启动时从数据库拉取所有colors的唯一值,动态创建Enum类型作为接口参数类型。如果值会更新,可搭配定时任务刷新枚举。

核心代码示例

from enum import Enum
from sqlalchemy import distinct
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from db import engine, SessionLocal
from models import Example, ExampleModel, Base

app = FastAPI()

# 动态创建Color枚举的函数
def create_dynamic_color_enum(db: Session):
    color_values = db.execute(select(distinct(Example.colors))).scalars().all()
    return Enum('DynamicColor', {value.upper(): value for value in color_values}, type=str)

# 全局存储动态枚举,启动时初始化
DynamicColor = None

@app.on_event("startup")
def on_startup():
    Base.metadata.create_all(bind=engine)
    db = SessionLocal()
    global DynamicColor
    DynamicColor = create_dynamic_color_enum(db)
    db.close()

# 可选:添加定时任务刷新枚举(需安装APScheduler)
# from apscheduler.schedulers.asyncio import AsyncIOScheduler
# scheduler = AsyncIOScheduler()
# scheduler.add_job(refresh_color_enum, 'interval', minutes=30)
# scheduler.start()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/colors", response_model=list[ExampleModel])
def read_colors(color: DynamicColor, db: Session = Depends(get_db)):
    return db.execute(select(Example).where(Example.colors == color)).scalars().all()

方案二:修改OpenAPI规范(适合值频繁更新的场景)

重写FastAPI的openapi()方法,每次生成文档时从数据库拉取最新的colors值,直接修改接口参数的schema,实现下拉选项的实时同步。

核心代码示例

from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from sqlalchemy import select, distinct
from db import engine, SessionLocal
from models import Example, ExampleModel, Base

app = FastAPI()

@app.on_event("startup")
def on_startup():
    Base.metadata.create_all(bind=engine)

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# 自定义OpenAPI生成逻辑
def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = app.openapi()
    # 从数据库获取最新的color唯一值
    db = SessionLocal()
    color_values = db.execute(select(distinct(Example.colors))).scalars().all()
    db.close()
    # 给/colors接口的color参数添加枚举属性
    path = "/colors"
    if path in openapi_schema["paths"]:
        get_operation = openapi_schema["paths"][path]["get"]
        for param in get_operation["parameters"]:
            if param["name"] == "color":
                param["schema"]["enum"] = color_values
                param["schema"]["type"] = "string"
    app.openapi_schema = openapi_schema
    return app.openapi_schema

app.openapi = custom_openapi

@app.get("/colors", response_model=list[ExampleModel])
def read_colors(color: str, db: Session = Depends(get_db)):
    return db.execute(select(Example).where(Example.colors == color)).scalars().all()

内容的提问来源于stack exchange,提问作者moth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 02:52:47