flask踩坑记录:sqlalchemy出现DetachedInstanceError的解决方法

最近写flask遇到了sqlalchemy抛出DetachedInstanceError异常

sqlalchemy.orm.exc.DetachedInstanceError: Instance <StockMonitor at 0x2139afa1508> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: http://sqlalche.me/e/13/bhk3)

问题原因 

class DetachedInstanceError(sa_exc.SQLAlchemyError):
    """An attempt to access unloaded attributes on a
    mapped instance that is detached."""

DetachedInstanceError是说在访问一个已经被卸载的属性。sqlalchemy使用session来记录一些信息,而commit动作会将session内的所有对象进行提交并删除,这就使得后续再从session里访问不到对象。

问题分析

对照代码来看一下:

这里在记录日志的时候其实是使用ORM来把日志记录到了数据库中

 

 

 

 在记录日志时确实是有commit操作,导致清空了session

解决方法 

 百度到了几种方法,大致是两个思路:

一种是:既然add对象被commit清掉了,那我添加两次总行了吧

这种不适合我的逻辑。如果想看这种方法如何解决问题的参考这个文章:sqlalchemy.orm.exc.DetachedInstanceError

另一种是:session里的内容被清掉了,那我不让它在commit的时候被清理掉就可以了吧

操作方式就是把:

Session=sessionmaker(bind=eng)

 改为:

Session=sessionmaker(bind=eng,expire_on_commit=False)

就短短两句话,看得我是不明觉厉...sqlalchemy.orm.exc.DetachedInstanceError: 错误解决

可惜我不是自己实例化的session,也没找到 Session=sessionmaker(bind=eng)在哪,反正思路是有了,就是给session加一个expire_on_commit=False的属性

 在sqlalchemy源码的Session类的实例化方法中可以找expire_on_commit的解释:

:param expire_on_commit:  Defaults to ``True``. When ``True``, all
   instances will be fully expired after each :meth:`~.commit`,
   so that all attribute/object access subsequent to a completed
   transaction will load from the most recent database state.

 大意是说当expire_on_commit=True时,commit提交时所有实例都会过期,之后再访问这些过期实例的属性时,sqlalchemy会重新去数据库加载最近的实例对应的数据记录

虽然没地方给Session加上expire_on_commit=False的属性,但其实可以在实例化SQLAlchemy对象时为session添加内容:

然后问题就解决了.........

总结

 为了避免在commit后无法再使用需要的属性,可以通过给Session加上expire_on_commit=False的方式来实现

但感觉这种解决方式还是有点粗暴的,问题出现在query和insert是两个对象但使用同一个session,应该使用不同的线程来进行操作。这个等以后对sqlalchemy了解更深入之后再来优化吧

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值