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

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

sqlalchemy.orm.exc.DetachedInstanceError: Instance  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里访问不到对象。

问题分析

对照代码来看一下:

flask踩坑记录:sqlalchemy出现DetachedInstanceError的解决方法_第1张图片

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

 flask踩坑记录:sqlalchemy出现DetachedInstanceError的解决方法_第2张图片

 flask踩坑记录:sqlalchemy出现DetachedInstanceError的解决方法_第3张图片

 flask踩坑记录:sqlalchemy出现DetachedInstanceError的解决方法_第4张图片

 在记录日志时确实是有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,flask,python,后端,sqlalchemy)