fastapi: 用lifespan在应用启动时初始化数据(数据播种)

一,代码


# 初始化必需的数据:添加一个管理员账号
async def init_data():
    """执行具体的异步数据逻辑"""
    async with AsyncSessionLocal() as db:
        async with db.begin():  # 开启事务
            # 使用 2.0 的 select 写法检查管理员是否存在
            admin_stmt = select(User).where(User.username == "admin")
            # admin = await db.execute(stmt).scalar_one_or_none()  # 获取单个对象,没有则返回 None
            result = await db.execute(admin_stmt)
            admin = result.scalar_one_or_none()

            if not admin:
                admin_user = User(
                    username="admin",
                    password='$2b$12$fJWexLMrpZPxu94E4flgMOZS8iJHl7ZH9WB2VCzj0gdrJM2F7Psry',  # 123456
                    nickname="管理员",
                    status=1
                )
                db.add(admin_user)
                db.commit()
            else:
                print("管理员账号已存在,跳过初始化。")

# 定义一个全局的 Redis 连接池变量
redis_pool: aioredis.ConnectionPool | None = None

# 1. 使用 Lifespan 管理管理 Redis 连接池的生命周期
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    global redis_pool

    # 启动时初始化数据
    # Base.metadata.create_all(bind=engine)
    # --------- 关键修改:异步建表方式 ---------
    async with engine.begin() as conn:
        # run_sync 会在异步连接中以同步方式安全地执行建表
        await conn.run_sync(Base.metadata.create_all)
        # ----------------------------------------
    await init_data()

    # 1,检查目录是否存在,如果不存在则创建
    # 【启动时执行】
    print("lifespan开始执行:")
    # UPLOAD_DIR = "/data/file/fast/images"
    # exist_ok=True 极其重要,能完美避免多进程并发创建时的报错
    upload_dir = settings.UPLOAD_DIR
    print("上传目录:", upload_dir)
    os.makedirs(upload_dir, exist_ok=True)
    print(f"初始化:确保目录 {upload_dir} 已存在")

    print("正在连接 Redis...")
    # 2,初始化redis的异步连接池
    redis_pool = aioredis.ConnectionPool(
        host="localhost",
        port=6379,
        db=0,
        decode_responses=True  # 自动将字节解码为字符串
    )
    yield
    # 应用关闭时,优雅地关闭连接池
    print("正在关闭 Redis 连接...")
    await redis_pool.disconnect()

二,说明

1,在 FastAPI 框架中,导入初始化数据(通常称为数据播种,Data Seeding)。

推荐做法:利用 FastAPI 的 lifespan 机制(或者老版本的 @app.on_event("startup")),在应用启动时检查数据库。如果发现某张表为空,就自动写入初始数据。

优点:自动化程度高,服务只要跑起来就能确保基础数据存在,对容器化部署(如 Docker)非常友好。
缺点:每次启动都会查询一次数据库,虽然开销不大,但在超大规模并发扩展时需要注意分布式锁问题(避免多个实例同时写)。

2, 别漏掉 await:在异步中,诸如 db.execute()、db.commit()、engine.begin() 前面必须加上 await。
    漏掉的话 Python 并不会报错,而是会返回一个协程对象(Coroutine),导致数据根本没有真正写入数据库。

3, db.begin() 上下文管理器:例子中使用了 async with db.begin():。这是 SQLAlchemy 推荐的事务写法。
    在这个代码块里,如果中间发生任何异常(比如读 JSON 失败),整个事务会自动回滚(Rollback);
    如果顺利执行完毕,它会自动提交(Commit),省去了手动写 try...except...commit() 的麻烦。

 

posted @ 2026-07-23 14:38  刘宏缔的架构森林  阅读(1)  评论(0)    收藏  举报