2013-03-12 73 views
4

我写了自己的金字塔ISessioninterface的实现,它应该将Session存储在数据库中。一切工作真的很好,但不知何故pyramid_tm抛出这个。一旦它被激活时,这样说:使用SQLAlchemy创建金字塔会话时发生DetachedInstanceError

DetachedInstanceError: Instance <Session at 0x38036d0> is not bound to a Session; 
attribute refresh operation cannot proceed 

(不要误会所在位置:<Session ...>是模型中,“......一个会话”最可能指的SQLAlchemy的会话类名(我称之为DBSession以避免混淆)。

我已经通过邮件列表看起来和SO,似乎只要有人有问题,他们是

  • 产生一个新的线程或
  • 手册ly电话transaction.commit()

我没有这些事情。然而,这里的特色是,我的会话被金字塔传递了很多。首先我做DBSession.add(session)然后return session。我可以事后处理会话,刷新消息等。

但是,似乎一旦请求完成,我就会得到这个异常。这里是完整的回溯:

Traceback (most recent call last): 
    File "/home/javex/data/Arbeit/libraries/python/web_projects/pyramid/lib/python2.7/site-packages/waitress-0.8.1-py2.7.egg/waitress/channel.py", line 329, in service 
    task.service() 
    File "/home/javex/data/Arbeit/libraries/python/web_projects/pyramid/lib/python2.7/site-packages/waitress-0.8.1-py2.7.egg/waitress/task.py", line 173, in service 
    self.execute() 
    File "/home/javex/data/Arbeit/libraries/python/web_projects/pyramid/lib/python2.7/site-packages/waitress-0.8.1-py2.7.egg/waitress/task.py", line 380, in execute 
    app_iter = self.channel.server.application(env, start_response) 
    File "/home/javex/data/Arbeit/libraries/python/web_projects/pyramid/lib/python2.7/site-packages/pyramid/router.py", line 251, in __call__ 
    response = self.invoke_subrequest(request, use_tweens=True) 
    File "/home/javex/data/Arbeit/libraries/python/web_projects/pyramid/lib/python2.7/site-packages/pyramid/router.py", line 231, in invoke_subrequest 
    request._process_response_callbacks(response) 
    File "/home/javex/data/Arbeit/libraries/python/web_projects/pyramid/lib/python2.7/site-packages/pyramid/request.py", line 243, in _process_response_callbacks 
    callback(self, response) 
    File "/home/javex/data/Arbeit/libraries/python/web_projects/pyramid/miniblog/miniblog/models.py", line 218, in _set_cookie 
    print("Setting cookie %s with value %s for session with id %s" % (self._cookie_name, self._cookie, self.id)) 
    File "build/bdist.linux-x86_64/egg/sqlalchemy/orm/attributes.py", line 168, in __get__ 
    return self.impl.get(instance_state(instance),dict_) 
    File "build/bdist.linux-x86_64/egg/sqlalchemy/orm/attributes.py", line 451, in get 
    value = callable_(passive) 
    File "build/bdist.linux-x86_64/egg/sqlalchemy/orm/state.py", line 285, in __call__ 
    self.manager.deferred_scalar_loader(self, toload) 
    File "build/bdist.linux-x86_64/egg/sqlalchemy/orm/mapper.py", line 1668, in _load_scalar_attributes 
    (state_str(state))) 
DetachedInstanceError: Instance <Session at 0x7f4a1c04e710> is not bound to a Session; attribute refresh operation cannot proceed 

对于这种情况,我停用了调试工具栏。一旦激活它,错误就会从那里抛出。看来这里的问题是在任何时候访问对象。

我意识到我可以尝试以某种方式分离它,但是这看起来不是正确的方式,因为如果不明确地将其添加到会话中,则无法修改该元素。

所以,当我没有产生新的线程,我没有明确地调用提交,我猜事务是在请求完全消失之前提交的,然后再次访问它。我如何处理这个问题?

回答

4

我相信你在这里看到的是一个事实,即响应回调和完成的回调实际上是在补间后执行的事实。它们位于应用程序的出口和中间件之间。作为补间的pyramid_tm在响应回调执行之前提交事务 - 在稍后访问时导致错误。

获得正确的这些东西的顺序是困难的。我的头顶上的一个可能性是注册您自己的补间根据pyramid_tm在会话上执行刷新,抓取id,并设置cookie上的响应。

我同情这个问题,因为事务发生后发生的任何事情都是金字塔中的一个真实灰色区域,并不总是清楚会话不应该被触及。我将记录下如何在未来继续考虑如何改善金字塔的工作流程。

+0

非常感谢。我研究了这个问题,补间的事情似乎可以解决它。但是,我揉揉了一个更容易的解决方案(请参阅我的答案)。 – javex 2013-03-12 17:23:43

2

我第一次尝试注册补间,它以某种方式工作,但数据没有得到保存。然后我st the于SQLAlchemy Event System。我发现了after_commit事件。使用这个,我可以在提交完成pyramid_tm之后设置会话对象的分离。我认为这提供了充分的灵活性,并且不对订单施加任何要求。

我的最终解决方案:

from sqlalchemy.event import listen 
from sqlalchemy.orm import Session as SASession 
def detach(db_session): 
    from pyramid.threadlocal import get_current_request 
    request = get_current_request() 
    log.debug("Expunging (detaching) session for DBSession") 
    db_session.expunge(request.session) 
listen(SASession, 'after_commit', detach) 

唯一的缺点:它需要调用get_current_request()这是气馁。但是,我没有看到以任何方式传递会话,因为事件被SQLAlchemy调用。我想到了一些丑陋的包装材料,但我认为这将是危险和不稳定的方式。

+0

好的解决方案!您还将获得额外的好处,即如果发生异常,您的会话更改将不会被存储,因为'pyramid_tm'将放弃您的交易而不是提交它。 – 2013-03-12 18:22:51

+0

不幸的是,这是行不通的,因为如果没有发生任何提交,它不会触发事件,因此它不会随后分离对象。问题似乎更加复杂,我可能会回到补间,但我仍然需要做一些试验 – javex 2013-03-13 22:46:16