fastapi教程-进阶五(Response Model)

本文介绍了FastAPI中的Response Model,通过示例展示了如何使用`response_model_exclude_unset`、`response_model_include`和`response_model_exclude`来控制响应体的结构。解释了这些参数如何用于排除默认值、未设置的参数以及指定要包含或排除的字段,以确保数据安全并优化JSON响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考内容

Response Model

还是以一个例子开头:

from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Optional[str] = None


# Don't do this in production!
@app.post("/user/", response_model=UserIn)
async def create_user(user: UserIn):
    return user

我们可以在下面这几个操作中用response_model参数来声明响应的模板结构:

  • @app.get()
  • @app.post()
  • @app.put()
  • @app.delete()
  • 其他请求类型

FastAPI将使用response_model进行以下操作:

  • 将输出数据转换为response_model中声明的数据类型。
  • 验证数据结构和类型
  • 将输出数据限制为该model定义的
  • 添加到OpenAPI中
  • 在自动文档系统中使用。

我们尝试启动上面这个例子的服务,并通过POST请求http://127.0.0.1:8000/user/,我们可以看到返回和请求体是一样的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1sVQyk8E-1599029098971)(evernotecid://FBE381A3-17C7-41D9-AA37-9C5F29FAB396/appyinxiangcom/20545635/ENResource/p216)]

不难发现这里有一个问题,如果我们用同一个model去声明请求体参数和响应model,会造成password的泄露,那么,我们来尝试不让password返回:

from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Optional[str] = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: Optional[str] = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
    return user

这里我们定义了两个model(UserIn和UserOut)分别用在请求体参数和响应model,再次请求http://127.0.0.1:8000/user/,这时我们发现响应已经没有password了:

{
  "username": "string",
  "email": "user@example.com",
  "full_name": "string"
}
response_model_exclude_unset

通过上面的例子,我们学到了如何用response_model控制响应体结构,但是如果它们实际上没有存储,则可能要从结果中忽略它们。

例如,如果model在NoSQL数据库中具有很多可选属性,但是不想发送很长的JSON响应,其中包含默认值。

我们来看一个例子:

from typing import List, Optional

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: float = 10.5
    tags: List[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]

我们发现,get操作中多了response_model_exclude_unset属性,他是用来控制什么的呢?

尝试请求http://127.0.0.1:8000/items/foo,结果返回了这样的数据:

{
  "name": "Foo",
  "price": 50.2
}

通过前面的学习,我们知道response的结构应该是这样的:

{
  "name": "string",
  "description": "string",
  "price": 0,
  "tax": 0,
  "tags": [
    "string"
  ]
}

这就是response_model_exclude_unset发挥了作用。response_model_exclude_unset可以控制不返回没有设置的参数。
当然,除了response_model_exclude_unset以外,还有response_model_exclude_defaultsresponse_model_exclude_none,我们可以很直观的了解到他们的意思,不返回是默认值的字段和不返回是None的字段,下面让我们分别尝试一下

设置response_model_exclude_defaults=True, 请求http://127.0.0.1:8000/items/baz,返回:

{
  "name": "Baz",
  "price": 50.2
}

设置response_model_exclude_none=True, 请求http://127.0.0.1:8000/items/baz,返回:

{
  "name": "Baz",
  "price": 50.2,
  "tax": 10.5,
  "tags": []
}

注意:response_model_exclude_defaults控制了不返回是默认值的字段,这个默认值不仅仅是None,他可能是我们在model中设置的任意一个值

response_model_include和response_model_exclude

接下来我们学习如何用response_model_includeresponse_model_exclude来控制响应参数,不难理解这两个参数的意思分别是响应参数包括哪些和不包括哪些,我们还是从实例看看他们是如何使用的:

from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: float = 10.5


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
    "baz": {
        "name": "Baz",
        "description": "There goes my baz",
        "price": 50.2,
        "tax": 10.5,
    },
}


@app.get(
    "/items/{item_id}/name",
    response_model=Item,
    response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
    return items[item_id]


@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
    return items[item_id]

通过前面的学习,我们知道了这里响应的结构应该是:

{
  "name": "string",
  "description": "string",
  "price": 0,
  "tax": 0
}

那么,我们来尝试一下请求http://127.0.0.1:8000/items/foo/name, 返回:

{
  "name": "Foo",
  "description": null
}

为什么只返回了namedescription呢?因为我们在参数中添加了response_model_include={"name", "description"},它控制了响应参数只包括namedescription
再次尝试请求http://127.0.0.1:8000/items/bar/public,返回:

{
  "name": "Bar",
  "description": "The Bar fighters",
  "price": 62
}

为什么没有返回tax呢?这时因为我们在参数中添加了response_model_exclude={"tax"},它控制了想响应参数除了tax都要返回。

除了用set来声明response_model_excluderesponse_model_include,也可以用listtuple,fastapi会将他们专程set类型

总结

  1. 使用路径操作装饰器的参数response_model定义响应模型,确保私有数据被过滤掉
  2. 使用路径操作装饰器的参数response_model_exclude_unsetresponse_model_exclude_defaultsresponse_model_exclude_none来过滤掉符合条件的参数
  3. 使用路径操作装饰器的参数response_model_includeresponse_model_exclude来指定字段进行过滤

上述栗子均放到git上啦,地址:戳这里

<think>好的,用户之前使用Flask,现在想迁移到FastAPI,需要了解如何用FastAPI进行项目开发。首先,我需要确定用户的具体需求是什么。他们可能已经熟悉Flask的某些功能,比如路由、扩展、项目结构等,现在想知道在FastAPI中如何实现类似的结构,以及迁移过程中需要注意哪些点。 用户提到“重构迁移到FastAPI”,可能意味着他们现有的Flask项目较大,需要逐步迁移或者完全重写。这时候,我需要比较Flask和FastAPI的主要差异,特别是在异步支持、依赖注入、数据验证等方面。同时,用户可能关心项目结构的最佳实践,如何组织路由、模型、配置等。 接下来,我需要考虑用户可能遇到的问题。比如,如何在FastAPI中处理数据库迁移(之前的问题提到Alembic),如何集成ORM(如SQLAlchemy或Tortoise-ORM),以及如何处理认证授权等常见功能。另外,用户可能对FastAPI的自动文档生成(Swagger和ReDoc)感兴趣,这也是一个亮点。 用户可能没有明确提到的深层需求包括性能优化、异步请求处理、测试策略、部署方式等。这些都是迁移到FastAPI时需要考虑的重要因素。例如,FastAPI基于Starlette支持异步,这对于高并发应用可能很重要,但用户需要了解如何正确使用async/await。 我需要确保回答结构清晰,分步骤介绍项目结构、核心模块实现、迁移步骤、注意事项等。同时,提供具体的代码示例和工具推荐,比如使用Alembic进行数据库迁移,或者推荐使用Pydantic进行数据验证,这些都能帮助用户更快上手。 另外,用户之前使用Flask,可能习惯了某些扩展,比如Flask-SQLAlchemy、Flask-Login等,需要解释在FastAPI中对应的工具或实现方式。例如,FastAPI没有内置的ORM,但可以结合SQLAlchemy或Tortoise-ORM,而认证可以通过OAuth2PasswordBearer等实现。 最后,需要提醒用户在迁移过程中注意测试,逐步替换模块,利用FastAPI的优势如类型提示、自动验证文档等。同时,可能推荐一些最佳实践,比如使用环境变量管理配置,使用Docker容器化部署,以及性能监控等。 总结来说,我需要提供一个全面的指南,涵盖项目结构、核心功能实现、迁移策略、工具推荐以及注意事项,帮助用户顺利从Flask迁移到FastAPI,并充分利用FastAPI的特性提升项目质量。</think>以下是如何用 FastAPI 构建项目的完整指南,包含 **项目结构设计**、**核心功能实现** 和 **迁移注意事项**,适用于从 Flask 迁移到 FastAPI 的场景: --- ### 一、项目结构设计(推荐方案) ```bash ├── app │ ├── main.py # 入口文件 │ ├── config.py # 配置管理(环境变量/配置文件) │ ├── database.py # 数据库连接与 ORM 初始化 │ ├── models # 数据模型 │ │ └── user.py # 示例:用户模型 │ ├── schemas # Pydantic 数据校验模型 │ │ └── user.py # 示例:用户请求/响应模型 │ ├── routes # 路由模块化拆分 │ │ ├── users.py # 用户相关路由 │ │ └── items.py # 其他业务路由 │ ├── dependencies.py # 自定义依赖项(如认证) │ └── utils # 工具类(日志、加密等) ├── tests # 单元测试 ├── requirements.txt # 依赖列表 ├── alembic # 数据库迁移(需初始化) └── Dockerfile # 容器化部署 ``` --- ### 二、核心模块实现 #### 1. **入口文件 (`app/main.py`)** ```python from fastapi import FastAPI from app.routes import users, items from app.database import engine, Base # 初始化 FastAPI app = FastAPI(title="My FastAPI Project") # 创建数据库表(生产环境建议用 Alembic 迁移) Base.metadata.create_all(bind=engine) # 挂载路由 app.include_router(users.router, prefix="/api/users", tags=["users"]) app.include_router(items.router, prefix="/api/items", tags=["items"]) ``` --- #### 2. **数据库配置 (`app/database.py`)** ```python from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker # 异步引擎示例(需 SQLAlchemy 1.4+) # engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db") # 同步引擎(适合简单项目) SQLALCHEMY_DATABASE_URL = "sqlite:///./app.db" engine = create_engine(SQLALCHEMY_DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() # 依赖注入获取数据库会话 def get_db(): db = SessionLocal() try: yield db finally: db.close() ``` --- #### 3. **路由与业务逻辑 (`app/routes/users.py`)** ```python from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from app.schemas.user import UserCreate, UserResponse from app.models.user import User from app.database import get_db router = APIRouter() @router.post("/", response_model=UserResponse) def create_user(user: UserCreate, db: Session = Depends(get_db)): # 业务逻辑示例 db_user = db.query(User).filter(User.email == user.email).first() if db_user: raise HTTPException(status_code=400, detail="Email already registered") new_user = User(**user.dict()) db.add(new_user) db.commit() db.refresh(new_user) return new_user ``` --- #### 4. **Pydantic 数据模型 (`app/schemas/user.py`)** ```python from pydantic import BaseModel, EmailStr class UserBase(BaseModel): email: EmailStr username: str class UserCreate(UserBase): password: str class UserResponse(UserBase): id: int is_active: bool class Config: orm_mode = True # 允许从 ORM 对象自动转换 ``` --- ### 三、从 Flask 迁移的关键步骤 #### 1. **逐步替换策略** - **按模块迁移**:优先重构核心业务模块(如用户认证)。 - **并行运行**:使用反向代理(如 Nginx)将部分路由指向 FastAPI,逐步替换 Flask 路由。 - **共享数据库**:保持原有数据库结构,减少数据迁移成本。 --- #### 2. **功能对照实现** | Flask 功能 | FastAPI 替代方案 | |----------------------|-------------------------------------------| | `Flask-SQLAlchemy` | 原生 SQLAlchemy + `SessionLocal` 依赖注入 | | `Flask-Login` | JWT/OAuth2 + 自定义依赖项 | | `Flask-WTF` | Pydantic 模型 + 表单验证 | | `Flask-RESTful` | 原生 APIRouter + Pydantic 响应模型 | | `Flask-CORS` | `fastapi.middleware.cors.CORSMiddleware` | --- #### 3. **异步改造(可选)** 若需高性能异步支持: ```python # 使用 async def 定义路由 @router.get("/async-data") async def fetch_data(): result = await some_async_operation() return result # 异步数据库驱动(如 asyncpg + SQLAlchemy 1.4+) from sqlalchemy.ext.asyncio import AsyncSession async def get_async_db(): async with AsyncSession(engine) as session: yield session ``` --- ### 四、进阶工具推荐 1. **测试工具**: - `TestClient`:内置 HTTP 客户端 - `pytest`:配合 `pytest-asyncio` 测试异步代码 ```python from fastapi.testclient import TestClient client = TestClient(app) def test_create_user(): response = client.post("/api/users/", json={"email": "test@example.com", "password": "secret"}) assert response.status_code == 200 ``` 2. **部署优化**: - **容器化**:使用 `uvicorn` + `gunicorn` 多进程部署 ```Dockerfile FROM python:3.9 RUN pip install gunicorn uvicorn COPY . /app CMD ["gunicorn", "app.main:app", "--workers", "4", "--worker-class", "uvicorn.workers.UvicornWorker"] ``` 3. **监控**: - `Prometheus` + `Grafana`:集成 `starlette-prometheus` --- ### 、注意事项 1. **依赖注入理解**:FastAPI 的依赖注入系统比 Flask 的上下文更灵活,但需要适应新范式。 2. **类型提示强制使用**:充分利用 Pydantic 和类型检查(IDE 支持更友好)。 3. **中间件差异**:FastAPI 中间件基于 Starlette,语法类似但细节不同。 4. **文档自动化**:访问 `/docs` 和 `/redoc` 自动生成交互式 API 文档。 --- ### 六、迁移收益 1. **性能提升**:异步支持使高并发场景响应更快。 2. **代码健壮性**:类型提示 + Pydantic 减少运行时错误。 3. **标准化接口**:自动生成 OpenAPI 文档,便于前后端协作。 4. **现代特性**:原生支持 WebSocket、GraphQL 等扩展。 通过以上步骤,你可以高效地将 Flask 项目迁移到 FastAPI,同时享受更强的类型安全性和性能优势。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值