SQLAlchemy,获取未绑定到会话的对象

Posted

技术标签:

【中文标题】SQLAlchemy,获取未绑定到会话的对象【英文标题】:SQLAlchemy, get object not bound to a Session 【发布时间】:2011-11-24 08:21:41 【问题描述】:

我正在尝试从数据库中获取对象集合并将其传递给未连接到数据库的另一个进程。我的代码如下所示,但我不断收到:

sqlalchemy.exc.UnboundExecutionError: Instance <MyClass at 0x8db7fec> is not bound to a Session; attribute refresh operation cannot proceed

当我尝试查看get_list() 方法之外的列表元素时。

def get_list (obj):
    sesson = Session()
    lst = session.query(MyClass).all()
    session.close()
    return lst

但是,如果我使用这个:

def get_list_bis (obj)
    session = Session()
    return session.query(MyClass).all()

我可以使用这些元素,但担心会话的状态,因为它没有关闭。

我在这里错过了什么?

【问题讨论】:

我相当肯定原始代码还有更多内容,除非 SQLA 在编写后发生了变化,因为简单地关闭会话会删除剩余的对象并且 不会 过期属性。另一方面,提交或回滚确实会使 attrs 过期。 @IljaEverilä 这是近 8 年前的问题。我很确定 SQLAlchemy 在那段时间确实发生了变化⸮ 只是为了确保我在 0.5(11 岁)上进行了测试,但无法重现。关闭不会使属性过期,即使在当时也是如此,并且正如 cmets 中对接受的答案 Session.close() 所指出的那样(并且当时确实)expunge_all() 隐含地。 但是,可能发生的情况是在关闭会话后访问了一个延迟加载的relationship 属性,这会导致上述错误。这就是为什么它会在 SQLA 问题中有所帮助,如果有问题的模型是问题的一部分。 @IljaEverilä 这是可能的。但是,我不确定我是否还有那个代码。就像我说的,那是几年前的事了。 ☹ 【参考方案1】:

如果您希望通过查询会话生成的一堆对象在会话范围之外可用,则需要为会话expunge 它们。

在您的第一个函数示例中,您需要添加一行:

session.expunge_all()

之前

session.close()

更一般地说,假设会话没有像第一个示例那样立即关闭。也许这是一个在整个 Web 请求或类似情况下保持活动状态的会话。在这种情况下,你不想做expunge_all。你会想要更多的外科手术:

for item in lst:
    session.expunge(item)

【讨论】:

但是...但是...“close() 方法发出一个 expunge_all(),并释放所有事务/连接资源。”此声明在您引用的页面上,至少可以追溯到 0.6 版。 正如上面的评论所指出的,这个答案并不完全正确。简单地关闭会话应该会使属性过时,但可以使用。 Session.commit()Session.rollback() 过期 ORM 加载状态,给定默认配置,并在提交之前清除以防止过期,使属性处于陈旧状态。 我也发现这没有帮助。我删除和或关闭并没有帮助。我了解它已过时且未连接到数据库,那又如何!我想查找,然后关闭。 db session 尽快将连接返回到池并继续使用“陈旧”对象只是为了返回 api 响应作为 json,很奇怪这必须抛出异常。【参考方案2】:

这通常是由于对象处于expired 状态而发生的,例如在committing 之后对象会过期,然后当这些过期 对象即将被使用时,ORM 会尝试refresh它们,但是当对象从会话中分离时(例如,因为该会话已关闭),这是无法做到的。可以通过使用expire_on_commit=False 参数创建会话来管理此行为。

>>> from sqlalchemy import inspect
>>> insp = inspect(my_object)
>>> insp.expired
True  # then it will be refreshed...

【讨论】:

【参考方案3】:

在我的例子中,我也保存了一个相关实体,这个秘籍帮助我 refresh 会话中的所有实例,利用 Session 是可迭代的事实:

map(session.refresh, iter(session))  # call refresh() on every instance

这是非常无效的,但有效。对于单元测试应该没问题。

最后说明:在 Python3 中,map() 是一个生成器,不会做任何事情。使用真正的列表推导循环

【讨论】:

无论如何,如果你不关心结果,你不应该使用map

以上是关于SQLAlchemy,获取未绑定到会话的对象的主要内容,如果未能解决你的问题,请参考以下文章

ORM框架SQLAlchemy学习(未整理完)

sqlalchemy : 使用参数绑定执行原始 sql

sqlalchemy.exc.ArgumentError:此 text() 构造未定义名为“current_time”的绑定参数

ORM框架SQLAlchemy

SQLAlchemy框架用法详解

SQLAlchemy