带有常规属性的SQLAlchemy DetachedInstanceError(不是关系)

har*_*dsv 44 python sqlalchemy

我刚开始使用SQLAlchemy并获得DetachedInstanceError,并且无法在任何地方找到相关信息.我在会话之外使用实例,所以SQLAlchemy很自然无法加载任何关系,如果它们尚未加载,但是,我访问的属性不是关系,实际上这个对象根本没有任何关系.我找到了诸如渴望加载的解决方案,但我无法应用于此,因为这不是一种关系.我甚至在关闭会话之前尝试"触摸"此属性,但它仍然无法阻止异常.即使在非关系属性之前成功访问过一次之后,可能导致此异常的原因是什么?任何有关调试此问题的帮助表示赞赏.我将同时尝试获得可重现的独立场景并在此更新.

更新:这是具有几个堆栈的实际异常消息:

  File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/attributes.py", line 159, in __get__
    return self.impl.get(instance_state(instance), instance_dict(instance))
  File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/attributes.py", line 377, in get
    value = callable_(passive=passive)
  File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/state.py", line 280, in __call__
    self.manager.deferred_scalar_loader(self, toload)
  File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/mapper.py", line 2323, in _load_scalar_attributes
    (state_str(state)))
DetachedInstanceError: Instance <ReportingJob at 0xa41cd8c> is not bound to a Session; attribute refresh operation cannot proceed
Run Code Online (Sandbox Code Playgroud)

部分模型如下所示:

metadata = MetaData()
ModelBase = declarative_base(metadata=metadata)

class ReportingJob(ModelBase):
    __tablename__ = 'reporting_job'

    job_id         = Column(BigInteger, Sequence('job_id_sequence'), primary_key=True)
    client_id      = Column(BigInteger, nullable=True)
Run Code Online (Sandbox Code Playgroud)

并且字段client_id是导致此异常的原因,其用法如下所示:

查询:

    jobs = session \
            .query(ReportingJob) \
            .filter(ReportingJob.job_id == job_id) \
            .all()
    if jobs:
        # FIXME(Hari): Workaround for the attribute getting lazy-loaded.
        jobs[0].client_id
        return jobs[0]
Run Code Online (Sandbox Code Playgroud)

这是稍后在会话范围之外触发​​异常的原因:

        msg = msg + ", client_id: %s" % job.client_id
Run Code Online (Sandbox Code Playgroud)

har*_*dsv 60

我在尝试缩小导致异常的代码时找到了根本原因.我在会话结束后在不同的地方放置了相同的属性访问代码,发现在查询会话结束后它肯定不会立即引起任何问题.事实证明,在关闭为更新对象而打开的新会话后,问题开始出现.一旦我理解了在会话结束后对象的状态不可用,我就能找到讨论同一问题的这个线程.这个问题的两个解决方案是:

  • 保持会话开放(这是显而易见的)
  • 指定expire_on_commit=Falsesessionmaker().

第三个选项是在创建会话后手动设置expire_on_commitFalse会话,如:session.expire_on_commit = False.我证实这解决了我的问题.

  • `session.expunge_all()`会有帮助吗? (5认同)
  • `session.flush()`应该做你想要的 (2认同)
  • 如果有人想知道,为什么默认情况下提交会使所有活动实例过期:“这样,当下次访问实例时,无论是通过属性访问还是通过它们出现在查询结果集中,它们都会收到最多的最近的状态。” https://docs.sqlalchemy.org/en/latest/orm/session_basics.html#committing (2认同)

gly*_*bet 10

我们得到了类似的错误,即使expire_on_commit设置为False.最后,它实际上是由于两个sessionmakers都被用来在不同的请求中进行会话.我真的不明白发生了什么,但如果你看到这个例外expire_on_commit=False,请确保你没有sessionmaker初始化.