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() 的麻烦。
浙公网安备 33010602011771号