sqlalchemy对象如何分离?

Kyo*_*reG 6 python sqlalchemy flask flask-sqlalchemy

我的代码结构如下:

project
--app
----utils
------util.py
----__init__.py
----models.py
--tests
----__init__.py
Run Code Online (Sandbox Code Playgroud)

在tests/__ init__.py中,我有一些代码可以通过从app/__ init__.py导入所有应用程序来初始化应用程序(如果重要的话,那就是瓶子)和数据库会话.我可以在tests/__ init__.py中创建模型,查询和访问backrefs的实例.以下表格的代码可以正常工作:

objs = SomeModel.query.all()
for o in objs:
    o.backref
Run Code Online (Sandbox Code Playgroud)

但是,如果我这样做:

from utils.util import some_function
objs = SomeModel.query.all()
for o in objs:
    some_function(o)
Run Code Online (Sandbox Code Playgroud)

some_function只是访问backref

def some_function(obj):
    obj.backref
Run Code Online (Sandbox Code Playgroud)

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

阅读sqlalchemy文档表明我需要将对象重新关联到数据库sesssion.我这样做了,看起来它的工作原理(即运行该函数不会因上一个错误而失败):

import db_session
def some_function(obj):
    db_session.add(obj)
    obj.backref
Run Code Online (Sandbox Code Playgroud)

那么当一个物体到底是什么时候分开了?似乎只是将对象传递给另一个模块中的函数将其从会话中分离出来.对象是否不知道与之关联的sqlalchemy会话?我试图避免这样做db_session.add(obj)似乎很多样板代码.

TCA*_*n07 11

我在处理我自己关于属性过期和实例分离的类似问题时遇到了这个问题univerio给了我一个很好的答案,从我学到的东西来看,我也许可以对你的问题有所了解。

在我的情况下,我正在创建,提交或回滚,然后Session在单个with...as...子句的范围内关闭所有,然后尝试访问我保存的实例(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,其obj被相关不到期自动属性。由于没有到期obj,错误不会被抛出,obj仍然会分离,这意味着当你叫obj.backref返回的值可能是不正确的/过时。您在问题中询问了分离,但到期是一个相关但不完全相同的概念。

旁白——如何设置obj不过期:要么在Session的初始化

my_session = sessionmaker(expire_on_commit=False
Run Code Online (Sandbox Code Playgroud)

sessionmaker的初始化

my_sessionmaker = sqlalchemy.orm.sessionmaker(expire_on_commit=False)
Run Code Online (Sandbox Code Playgroud)

甚至在Session已经实例化之后

my_session.expire_on_commit = False
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案。Session.add(detachedInstance) 帮助我在 Flask py.test 运行中摆脱了这些恼人的错误。我只想指出,用expire_on_commit=False 定义sessionmaker,虽然乍一看很好,但确实对数据库持久性产生了副作用,在我的例子中,导致了其他更难以修复的错误。例如,相同的代码将提交两次相同的实例,该实例具有通过 expire_on_commit=False 设置的唯一约束。 (2认同)