FastAPI教程-集成SQLAlchemy:expire_on_commit不要设置为True

说明

创建 session 工厂对象时,代码如下:

async_session = async_sessionmaker(
    async_engine,           # 绑定数据库引擎
    class_=AsyncSession,    # 指定会话类,调用 async_session() 返回的类型
    expire_on_commit=False
)

expire_on_commit 的默认值是 True,需要将 expire_on_commit 设置为 False

作用:执行 commit 后,会话中的 表模型对象 不过期,再次访问对象中的属性(即:表中的字段),不需要重新查询数据库

expire_on_commit 设置为 True

代码修改为:

async_session = async_sessionmaker(
    async_engine,           # 绑定数据库引擎
    class_=AsyncSession,    # 指定会话类,调用 async_session() 返回的类型
    expire_on_commit=True
)

或者去掉 expire_on_commit 参数

测试

修改 main.py 文件,内容如下:

@app.post('/user')
async def save_user(username:str,password:str, db = Depends(get_db)):
    user = User(username = username,password = password)
    await save(db,user)
    # 关键:在 save()中已经执行 commit() 了,之后再访问对象属性会报错
    return {
        "code":200,
        "msg":f"添加成功!用户id:{user.id}"
    }

关键:在 save() 中已经执行 commit() 了,之后再访问对象属性会报错,如:user.id

执行测试

报错

原因

  1. await save(db, user) 内部执行 await db.commit()
  2. 默认 expire_on_commit=True,执行 commit() 后, user 对象标记为 expired 过期
  3. return 里读取 user.id,对象过期,SQLAlchemy 尝试自动 SELECT 刷新对象
  4. AsyncSession 不能同步执行数据库 IO,直接抛 MissingGreenlet 错误

解决

解决方法有若干种,这里用:增加 await db.refresh(user)

代码

@app.post('/user')
async def save_user(username:str,password:str, db = Depends(get_db)):
    user = User(username = username,password = password)
    await save(db,user)
    # commit后再刷新模型对象,会自动查询
    await db.refresh(user)
    return {
        "code":200,
        "msg":f"添加成功!用户id:{user.id}"
    }

执行测试

程序能够正确执行,但看控制台,发现 commit() 后,又再次执行查询了:

结论

如果将 expire_on_commit 设置为 True ,commit 后模型对象已经过期,不能再用。
再次访问对象属性(即:表中的字段值),为了避免报错,需要加额外代码,导致再次查询数据

所以:expire_on_commit 设置为 False