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

如何在FastAPI中实现适配自定义格式的通用响应模型

通用响应模型实现方案

核心思路:利用Pydantic泛型(GenericModel)

FastAPI基于Pydantic,用GenericModel可以轻松实现通用化的响应结构,无需为每个业务模型重复编写响应模板。我们可以定义两个通用模型:一个处理单个对象,一个处理对象列表,同时支持动态指定数据字段名(比如object/objects)。

代码实现

1. 导入依赖

from typing import Generic, TypeVar, List, Optional
from pydantic import BaseModel, GenericModel
from fastapi import FastAPI

2. 定义泛型类型变量

# 定义泛型类型,代表任意业务模型
T = TypeVar('T')

3. 通用成功响应模型

class SingleResponse(GenericModel, Generic[T]):
    status: str = "success"
    data: dict[str, T]

class ListResponse(GenericModel, Generic[T]):
    status: str = "success"
    data: dict[str, List[T]]

4. 业务模型示例

class Object(BaseModel):
    id: str
    category: str
    field1: Optional[str] = None

5. 在FastAPI接口中使用

app = FastAPI()

# 单个对象接口
@app.get("/object/{obj_id}", response_model=SingleResponse[Object])
def get_object(obj_id: str):
    # 模拟数据库查询
    obj = Object(id=obj_id, category="test", field1="content")
    # 返回指定格式的字典,data里的键对应期望的字段名
    return {"data": {"object": obj}}

# 对象列表接口
@app.get("/objects", response_model=ListResponse[Object])
def get_objects():
    # 模拟列表数据
    objs = [
        Object(id="1", category="test", field1="content"),
        Object(id="2", category="test", field1="content")
    ]
    return {"data": {"objects": objs}}

优化:自动处理单数/复数字段名

如果想更自动化,避免手动写object/objects,可以加一个简单的工具函数来生成复数字段名:

def get_plural_name(model_class: type[BaseModel]) -> str:
    # 基础的单数转复数逻辑,可根据业务需求扩展
    class_name = model_class.__name__.lower()
    if class_name.endswith("y"):
        return class_name[:-1] + "ies"
    elif class_name.endswith(("s", "x", "z", "ch", "sh")):
        return class_name + "es"
    else:
        return class_name + "s"

接口中使用示例:

@app.get("/objects", response_model=ListResponse[Object])
def get_objects():
    objs = [Object(id="1", category="test"), Object(id="2", category="test")]
    plural_name = get_plural_name(Object)
    return {"data": {plural_name: objs}}

更简洁的方式:使用装饰器自动包装

如果觉得每个接口都写return {"data": {...}}麻烦,可以写一个装饰器自动处理响应包装:

from functools import wraps
import asyncio

def wrap_response(single_field: str = None, list_field: str = None):
    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            result = await func(*args, **kwargs) if asyncio.iscoroutinefunction(func) else func(*args, **kwargs)
            if isinstance(result, list):
                # 处理列表响应
                field = list_field
                if not field and result:
                    field = get_plural_name(result[0].__class__)
                elif not field:
                    field = "items"
                return {"status": "success", "data": {field: result}}
            else:
                # 处理单个对象响应
                field = single_field or result.__class__.__name__.lower()
                return {"status": "success", "data": {field: result}}
        return wrapper
    return decorator

使用装饰器的接口示例:

@app.get("/object/{obj_id}")
@wrap_response(single_field="object")
def get_object(obj_id: str):
    return Object(id=obj_id, category="test", field1="content")

@app.get("/objects")
@wrap_response(list_field="objects")
def get_objects():
    return [
        Object(id="1", category="test"),
        Object(id="2", category="test")
    ]

这样接口只需返回原始业务对象或列表,装饰器会自动包装成指定格式。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 13:10:48