11

我的代码结构如下:

project
--app
----utils
------util.py
----__init__.py
----models.py
--tests
----__init__.py

在 tests/__init__.py 中,我有代码通过从 app/__init__.py 全部导入来初始化应用程序(烧瓶,如果这很重要)和数据库会话。我可以在 tests/__init__.py 中创建模型、查询和访问 backrefs 的实例。以下形式的代码可以正常工作:

objs = SomeModel.query.all()
for o in objs:
    o.backref

但是,如果我这样做:

from utils.util import some_function
objs = SomeModel.query.all()
for o in objs:
    some_function(o)

其中 some_function 只是访问一个 backref

def some_function(obj):
    obj.backref

我收到一个错误,例如DetachedInstanceError: Parent instance <SomeModel at 0x2c1fe10> is not bound to a Session; lazy load operation of attribute 'backref' cannot proceed

阅读 sqlalchemy 文档表明我需要将对象重新关联到数据库会话。我这样做了,看起来它可以工作(即运行该函数不会因先前的错误而失败):

import db_session
def some_function(obj):
    db_session.add(obj)
    obj.backref

那么对象究竟什么时候分离呢?似乎只是将对象传递给另一个模块中的函数就会将其与会话分离。对象是否不知道与其关联的 sqlalchemy 会话?我试图避免这样做db_session.add(obj)似乎有很多样板代码。

4

1 回答 1

12

我在处理关于属性过期和实例分离的类似问题时遇到了这个问题。univerio给了我一个很好的答案,根据我一直在学习的内容,我也许可以对您的问题有所了解。

在我的情况下,我正在创建、提交或回滚,然后Session在单个with...as...子句的范围内关闭 all,然后尝试访问我保存的实例(obj在您的示例中),但在该子句的范围之外with。发生的事情是Session在我尝试引用保存的对象之前关闭。默认情况下,在 SQLAlchemy 中,没有active就无法访问持久的属性/对象,除非明确告知允许它 Session。这是通过强制应用程序首先查询/检索更新的数据来“保护”代码免于意外或在不知不觉中使用过时/不正确的数据,这需要关联的Session. 因此,在我的情况下,Session提交后保持打开状态意味着对象可以使用它Session来查询数据库,以防记录在首次写入后被修改。

在您的情况下,Session用于获取对象objs = SomeModel.query.all()的方法在查询之后但在obj.backref调用之前被关闭或断开连接(尽管我不确定如何;我不知道究竟SomeModel是什么,我假设是一个构造来自Flask,它Session在其背景中包含a )。所以obj不再有与数据库的连接,因此是“分离的”。通过将它添加到db_session您允许obj重新建立与其源数据库的连接,它可以通过它查询以检查更新的属性,因此它不再分离。

最后,值得一提的是,DetachedInstanceError可以通过指定与之关联的原始属性不自动过期Session属性来避免这种情况。通过不使过期,不会引发错误,仍会detached,这意味着当您调用返回值时可能是不正确/过时的。您在问题中询问了分离,但到期是一个相关但不相同的概念。objobjobjobj.backref

除了 - 如何设置obj不过期:要么在Session's 初始化

my_session = sessionmaker(expire_on_commit=False

sessionmaker初始化

my_sessionmaker = sqlalchemy.orm.sessionmaker(expire_on_commit=False)

甚至在Session已经实例化之后

my_session.expire_on_commit = False
于 2016-02-10T22:43:41.093 回答