You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI结合SQLAlchemy(SQLite)执行更新操作时出现不符合预期的结果

FastAPI结合SQLAlchemy(SQLite)执行更新操作时出现不符合预期的结果

你遇到的这个问题其实是SQLAlchemy的会话(Session)状态管理导致的,属于ORM里常见的“对象已被会话跟踪”的情况,我来给你一步步解释清楚:

问题场景回顾

你当前的执行流程是:

  1. 在main.py里调用crud.getdata从数据库拿到userdata(此时num是50)
  2. 直接修改userdata.num = 10,但这时候你还没提交到数据库
  3. 把修改后的userdata传给crud.updatedata,结果在这个函数里查询同一个uid的用户时,拿到的nuser居然已经是num=10的状态,而不是数据库里的50,最终计算后变成0,完全不符合你预期的50-10=40。

原因分析

SQLAlchemy的Session会跟踪所有从它里面查询出来的对象,当你在main.py里通过getdata拿到userdata后,这个对象就被当前的Session“托管”了。一旦你修改了这个对象的属性(比如userdata.num = 10),哪怕还没调用commit(),Session内部已经记录了这个修改。

这时候当你在同一个Session里再次查询同一个用户(updatedata里的db.query(...)),SQLAlchemy不会再去数据库里重新捞数据,而是直接返回Session已经托管的那个被修改过的对象——也就是你刚改成10的那个userdata,所以才会出现查询结果和数据库实际值不一致的情况。

解决办法

根据你的需求,有几种可行的处理方式:

方式1:分离操作数据与查询对象(推荐)

如果你只是想基于数据库原始值做计算,不要先修改查询出来的对象,而是把要操作的数值单独传进去,避免会话跟踪的影响:

# 修改后的main.py
@app.post("/test/{user}")
def cal(user: int, subtract_num: int, db: Session = Depends(get_db)):
    # 只传递用户uid和要减去的数值,而非修改后的对象
    user = crud.updatedata(db, user_uid=user, subtract_num=subtract_num)
    return user

# 修改后的crud.py
def updatedata(db: Session, user_uid: int, subtract_num: int):
    nuser = db.query(model.User).filter(model.User.uid == user_uid).one_or_none()
    if not nuser:
        # 处理用户不存在的情况
        raise HTTPException(status_code=404, detail="User not found")
    nuser.num -= subtract_num  # 直接基于数据库原始值计算
    db.commit()
    db.refresh(nuser)
    return nuser

方式2:将对象从会话中分离

如果你一定要在main.py里先修改对象再处理,可以用db.expunge()把对象从Session的跟踪中移除,这样后续查询就会重新从数据库获取数据:

# 修改后的main.py
@app.post("/test/{user}")
def cal(user: int, db: Session = Depends(get_db)):
    userdata = crud.getdata(db, user=user)
    userdata.num = 10
    # 解除会话对该对象的跟踪
    db.expunge(userdata)
    user = crud.updatedata(db, userdata)
    return user

这种方式会增加逻辑复杂度,一般不推荐优先使用。

方式3:强制查询数据库最新值

如果你需要确保查询到的是数据库的原始值,可以在查询时加上populate_existing(),强制忽略Session缓存的对象:

# 修改crud.py里的查询语句
nuser = db.query(model.User).filter(model.User.uid == user.uid).populate_existing().one_or_none()

总结

这不是你代码的“错误”,而是SQLAlchemy Session的**一级缓存(First-Level Cache)**特性导致的预期外行为——Session会缓存它查询过的对象来提升性能,但这也会导致在同一个会话内修改对象后,后续查询拿到的是修改后的缓存对象,而非数据库里的原始值。

备注:内容来源于stack exchange,提问作者user27208323

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 14:34:27