概述
最近写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了解更深入之后再来优化吧
最后
以上就是大气酸奶为你收集整理的flask踩坑记录:sqlalchemy出现DetachedInstanceError的解决方法问题原因 问题分析解决方法 总结的全部内容,希望文章能够帮你解决flask踩坑记录:sqlalchemy出现DetachedInstanceError的解决方法问题原因 问题分析解决方法 总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复