【问题标题】:DetachedInstanceError when creating Pyramid Session with SQLAlchemy使用 SQLAlchemy 创建金字塔会话时出现 DetachedInstanceError
【发布时间】:2013-02-27 09:25:34
【问题描述】:

我编写了自己的 Pyramid 的 ISession interface 实现,它应该将会话存储在数据库中。一切都很好,但不知何故pyramid_tm 抛出了这个。一旦它被激活,它就会这样说:

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

(不要在这里混淆:&lt;Session ...&gt; 是模型的类名,“... to a Session”很可能是指 SQLAlchemy 的 Session(我称之为 DBSession 以避免混淆)。

我查看了邮件列表和 SO,似乎任何时候有人遇到问题,他们都是

  • 产生一个新线程或
  • 手动拨打transaction.commit()

这两件事我都不做。然而,这里的特点是,我的会话被 Pyramid 传递了很多。首先我做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

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

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

因此,当我没有生成新线程并且我没有显式调用提交时,我猜想事务在请求完全消失之前提交,然后再次访问它。我该如何处理这个问题?

【问题讨论】:

    标签: python sqlalchemy pyramid zope


    【解决方案1】:

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

    让这些事情的顺序正确是很困难的。我想到的一种可能性是在pyramid_tmpyramid_tmpyramid_tmpyramid_tm 下注册您自己的补间,它会在会话上执行刷新、获取 id 并在响应中设置 cookie。

    我对这个问题表示同情,因为在提交事务后发生的任何事情都是 Pyramid 中的一个真正的灰色区域,在该区域中并不总是清楚不应该触及会话。我会做笔记以继续思考如何在未来改进 Pyramid 的这个工作流程。

    【讨论】:

    • 非常感谢。我调查了这个问题,补间的东西似乎可以解决它。但是,我偶然发现了一个更简单的解决方案(请参阅我的答案)。
    【解决方案2】:

    我首先尝试注册一个补间,它以某种方式工作,但数据没有得到保存。然后我偶然发现了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 调用。我想到了一些丑陋的包装材料,但我认为那会很冒险且不稳定。

    【讨论】:

    • 很好的解决方案!如果出现异常,您还将获得额外的好处,即不会存储对会话的更改,因为pyramid_tm 将中止您的事务而不是提交它。
    • 遗憾的是,这不起作用,因为如果没有提交发生,它不会触发事件,因此它不会在之后分离对象。问题似乎更复杂,我可能会回到补间,但我仍然需要做一些实验
    猜你喜欢
    • 2013-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-20
    • 2022-09-29
    • 2018-02-07
    • 2020-08-06
    相关资源
    最近更新 更多