Python 现代开发学习笔记

(十八)用应用工厂与 lifespan 组合项目

1. 在应用启动和结束时管理共享资源

路由都已经登记在各自的 router 上,最后由应用工厂组合。工厂让真实服务、临时测试库和演示使用同样的应用代码,只替换配置。

# 项目文件:api_project/src/bookapi/app.py
from contextlib import asynccontextmanager

from fastapi import FastAPI

from .config import Settings
from .database import open_database
from .errors import ErrorBody, install_handlers
from .routes import auth, books, exports


def create_app(settings: Settings | None = None) -> FastAPI:
    settings = settings or Settings()

    @asynccontextmanager
    async def lifespan(app: FastAPI):
        engine, factory = open_database(settings)
        app.state.settings = settings
        app.state.session_factory = factory
        try:
            yield  # Uvicorn 在此期间接收请求。
        finally:
            engine.dispose()  # 停止应用时释放连接池。

    app = FastAPI(
        title="个人书籍 API",
        version="1.0.0",
        lifespan=lifespan,
        responses={
            code: {"model": ErrorBody} for code in [401, 403, 404, 409, 422, 500]
        },
    )
    install_handlers(app)
    for router in [auth.router, books.router, exports.router]:
        app.include_router(router)
    return app

create_app() 返回一个新的 FastAPI 应用。include_router 将每组路由并入应用,最终路径包含各自的 prefix。路由文件通过相对导入复用模型、服务和依赖,知识关系可以直接沿导入查找。

responses 为常见错误状态声明 ErrorBody 模型,OpenAPI 中的 422 因而使用本项目的 error 结构。这项声明负责文档,实际错误仍由前面的异常处理器生成。

第五章用 contextlib.contextmanager 把生成器变成同步上下文管理器。这里导入的 asynccontextmanager 是它的异步版本,包装一个 async def 定义、含有一次 yield 的异步生成器。进入 async with 时运行到 yield,退出时从那里恢复并执行收尾;进入和退出过程中还可以用 await 等待资源操作。

FastAPI 按这种方式管理 lifespan:在接收请求前准备数据库,将配置和会话工厂放入 app.state;在 yield 处进入服务运行阶段;服务结束后恢复执行,释放连接池。这里使用同步数据库,所以准备和释放没有额外的 await。导入 bookapi 或 app.py 本身不会打开数据文件。

app.state.session_factory 是共享工厂,get_session 调用它才创建本次请求的工作对象。把一份 Session 直接放进 app.state,多个请求就会同时碰到同一组事务与对象状态;共享工厂则让创建出来的会话彼此独立。

应用工厂只组合对象,启动阶段建立引擎与工厂,请求阶段各自借会话,停止时在 lifespan 的 finally 释放连接池

生命周期接口可对照 FastAPI lifespan。数据库结构调整继续通过迁移完成;本项目第一次启动创建表,后续启动读取同一文件。

2. 目录中的职责连接

文件 负责的工作
schemas.py 输入、公开输出与字段规则
models.py 表结构、主键、外键和唯一约束
database.py、config.py 数据库位置、引擎与会话工厂
dependencies.py、security.py 本次会话、令牌检查与当前身份
service.py 归属、筛选与 CRUD 事务
routes/ HTTP 参数、状态、响应头和动作连接
errors.py 事务工具与错误响应
app.py、cli.py 应用组合、生命周期、初始化和服务入口
jobs.py 响应后的独立数据库任务
从 books 路由分别沿输入模型依赖链和 service 连接数据库,再经输出模型形成响应,应用工厂把路由及处理器统一组合

笔记正文与全部题解 · Ctrl / ⌘ K