【Flask转FastAPI-python知识点】

2 阅读5分钟

小编近期要着手重构项目,那我们就不能只停留在“怎么用”的层面,得深入到架构设计最佳实践的层面。

要把一个项目成功迁移到 FastAPI,需要的不只是语法知识,而是如何用 FastAPI 的思维(“FastAPI 式”的写法)去构建应用。

下面这份指南,我依据自己实习改写项目经验把它整理成了 “重构实战笔记” 的风格,同学们可以直接参考这个思路来规划你的代码结构。


🚀 FastAPI 进阶实战:从“能跑”到“优雅重构”

摘要:项目重构在即,如何充分利用 FastAPI 的异步特性与依赖注入系统?本文跳出基础语法,深入解析 Pydantic V2 新特性、依赖注入的高级用法、异步陷阱规避以及推荐的项目目录结构,助你打造高性能、易维护的现代化 API 服务。


前言

哈喽大家好,我是爱摸鱼的打工仔

最近我也在忙着把老项目往 FastAPI 上迁移。很多小伙伴(包括我自己)刚开始写 FastAPI 时,很容易把它当成“加了类型提示的 Flask”来写。虽然也能跑,但这样就浪费了 FastAPI 最强大的两个武器:依赖注入(Dependency Injection)异步原生支持

今天这篇笔记,不讲基础语法,专门聊聊重构时需要注意的“深水区”,帮你避坑,写出真正的 “FastAPI 风格” 代码。


核心思维转变:从“全局变量”到“依赖注入”

在 Flask 或 Django 中,我们习惯用全局变量或者 g 对象来传递数据库会话或当前用户信息。但在 FastAPI 中,依赖注入才是王道。

为什么要改?
重构时,你会发现把数据库连接、用户认证逻辑抽离成“依赖”,你的视图函数(路径操作函数)会变得极其干净,而且测试极其方便。

重构前(伪代码):

# 这种写法在 FastAPI 里不推荐,难以测试
@app.get("/users/{user_id}")
def get_user(user_id: int):
    db = SessionLocal() # 每次都要手动连数据库
    user = db.query(User).filter(User.id == user_id).first()
    return user

重构后(FastAPI 风格):

# 定义依赖(通常在 dependencies.py 或 crud.py 中)
def get_db():
    db = SessionLocal()
    try:
        yield db # 注意这里用 yield,可以在请求结束后自动关闭连接
    finally:
        db.close()

# 视图函数
@app.get("/users/{user_id}")
def get_user(user_id: int, db: Session = Depends(get_db)):
    # db 对象是自动注入的,函数签名即文档
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(status_code=404, detail="用户不存在")
    return user

打工仔划重点
重构时,把所有“获取资源”(数据库、Redis、当前登录用户)的逻辑都封装成 Depends。这样你的业务逻辑函数就纯粹得只剩下“处理数据”了。


数据验证升级:拥抱 Pydantic V2

FastAPI 强依赖 Pydantic。如果你之前的项目用的是 Pydantic V1,或者没有严格的数据验证,现在是升级的好时机。

关键变化
在重构数据模型(Schema)时,注意 Pydantic V2 的一些新特性,比如性能提升和更严格的类型检查。

  1. 分离输入与输出模型
    不要用一个类通吃。重构时,建议把模型拆细:

    • ItemCreate:创建时用的模型(比如密码必填)。
    • ItemUpdate:更新时用的模型(所有字段都 Optional,支持部分更新)。
    • ItemResponse:返回给前端的模型(包含 id, create_time,但不包含 password)。
  2. 利用 model_config

    from pydantic import BaseModel, ConfigDict
    
    class UserOut(BaseModel):
        # 允许从 ORM 对象直接读取属性
        model_config = ConfigDict(from_attributes=True)
        
        id: int
        username: str
        email: str
    

    这样你在返回数据库对象时,FastAPI 能自动把它转成 JSON,不用手写 dict() 了。


异步的坑:小心“阻塞”你的高性能

FastAPI 是异步框架,但这并不意味着你可以随便写 async/await

重构时的最大陷阱
如果你用了同步的数据库驱动(比如标准的 SQLAlchemypymysql),却把它放在 async def 函数里,整个服务会被卡死

解决方案

  1. 方案 A(推荐) :使用异步数据库驱动(如 databases 库或 SQLAlchemyasync 支持)。

    @app.get("/items/")
    async def read_items():
        # 真正的异步查询,不会阻塞主线程
        async with database:
            query = items.select()
            results = await database.fetch_all(query)
        return results
    
  2. 方案 B(兼容旧代码) :如果你暂时不想换数据库驱动,不要把函数定义成 async
    FastAPI 会在后台线程池中运行同步函数,从而避免阻塞。

    @app.get("/items/")
    def read_items(): # 注意这里是 def 而不是 async def
        # 同步代码在后台线程运行,安全
        results = db.query(Item).all()
        return results
    

打工仔划重点
重构时检查你的 IO 操作(数据库、文件读写、第三方 API 请求)。如果是同步库,就用 def;如果是异步库,就用 async def。千万别混用!


项目结构重构:拒绝“单文件走天下”

刚开始学习时,我们习惯把所有代码写在 main.py 里。但重构正式项目时,必须模块化。

推荐的标准目录结构(参考 FastAPI 官方推荐):

my_project/
├── app/
│   ├── __init__.py
│   ├── main.py          # 入口文件,创建 FastAPI 实例
│   ├── dependencies.py  # 存放依赖注入(get_db, get_current_user)
│   ├── models.py        # 数据库模型(ORM 表结构)
│   ├── schemas.py       # Pydantic 模型(数据验证)
│   ├── crud.py          # 数据库增删改查逻辑
│   ├── routers/         # 路由拆分
│   │   ├── items.py
│   │   └── users.py
│   └── core.py          # 配置信息(密钥、环境变量)
├── requirements.txt
└── .env

如何拆分路由?
app/routers/items.py 中:

from fastapi import APIRouter

router = APIRouter(prefix="/items", tags=["物品管理"])

@router.get("/")
def read_items():
    return [{"name": "Item 1"}, {"name": "Item 2"}]

app/main.py 中注册:

from fastapi import FastAPI
from app.routers import items, users

app = FastAPI()

app.include_router(items.router)
app.include_router(users.router)

这样拆分后,你的代码结构清晰,多人协作也不会冲突。


总结

重构不仅仅是换语法,更是思维的重塑

  1. 依赖注入 解耦业务逻辑和基础设施。
  2. Pydantic V2 严格定义数据边界,区分输入输出。
  3. 正确处理 异步与同步,避免性能陷阱。
  4. 合理规划 目录结构,让项目易于维护。

希望这些实战经验能帮你在重构路上少走弯路。FastAPI 真的很香,一旦上手,你会发现写 API 原来可以这么优雅!

我是爱摸鱼的打工仔,祝大家重构顺利,早点下班!👋