0

我想为了整洁,我会提出一个不同的问题。它基于: SQLAlchemy 不会更新我的数据库SQLAlchemy 会话:如何保持活动状态?.

所以这是交易:我有一个 Pyramid 应用程序正在与一个守护进程对话,而后者又与一个数据库对话。

现在由于某种原因,当我将它添加到数据库会话变量时,它没有被提交到数据库中,如下所示:

DBSession.add(ModelInstance)

调用 flush 或 commit 不会使其提交。

这就是我制作 DBSession 的方式:

    settings = {
        'sqlalchemy.url':'blah blah'
        }
    DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)

这对我来说似乎很好,因为我可以很好地查询数据库。即:这种事情有效:

DBSession.query(ModelClass).get(id)

这位好先生https://stackoverflow.com/users/100297/martijn-pieters建议使用以下代码:

import transaction
transaction.commit()

这可以很好地确保我的东西得到承诺。唯一的问题是它以某种方式使我的 DBSession 无用。因此,如果我想使用我的会话正在跟踪的对象,我需要重新实例化会话和这些项目。这很糟糕。它占用了大量的时间。

我的问题是,简而言之,我该如何避免这种情况?

长篇大论:

  • 如何让我的 DBSession 正确提交而不破坏它?

或者

  • 如何在不需要冗长的数据库调用的情况下修复我的 DBSession 和关联的模型实例?

  • 知道为什么会这样吗?我已经在我提到的 Pyramid 应用程序中以相同的方式成功地构建了一个 DBSession,它工作得非常好,它在我想要它的时候就提交了。

具体我遇到的错误可以参考我开头提到的两个问题

4

3 回答 3

2

根据sqlalchemy 的文档

的另一个行为commit()是,默认情况下,它会使提交完成后存在的所有实例的状态过期。这样当实例下次被访问时,无论是通过属性访问还是通过它们出现在Query结果集中,它们都会收到最新的状态。要禁用此行为,请sessionmaker()配置 expire_on_commit=False.

看似问题实际上是故意完成的:当您提交时,所有对象都被标记为过期。这样做是为了避免继续使用数据库中可能已更改的旧缓存值。

它在金字塔中工作的原因是因为每个请求都有自己的事务,并且在处理它们之前会查询对象。您尝试使用预览事务中的对象,这可能不是一个好主意,因为它们可能与数据库不同步。

为了解决您的问题,您可以确保在事务结束后不重用对象(可能需要您的事务包含更多内容),或者您可以expire_on_commit=False按照事务中的建议使用。但如果您使用后者,请注意该对象可能已过时。

于 2012-10-25T11:05:04.303 回答
2

在 SQLAlchemy 会话中,它们在提交时管理的对象过期。这是明智的,因为在提交之后,您无法保证在并发世界中其他东西不会改变他们试图在数据库中镜像的状态。

Pyramid 建议使用事务管理器来帮助您维护每个请求的单个事务。transaction.commit()请求完成后,它会自动呼叫您。这样,您不必考虑/担心对象过期,并且如果您的代码引发异常,事务将被正确中止。

设置事务管理器的方法是安装pyramid_tm然后zope.sqlalchemy连接DBSessionzope.sqlalchemy.ZopeTransactionExtension. 然后事情将“正常工作”。

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))

# ...

def main(global_conf, **settings):
    config = Configurator(...)
    config.include('pyramid_tm')
    # ...

如果您需要填充新对象的主键或确保某些 SQL 将正确执行,您可以使用DBSession.flush()在事务中执行 SQL 而无需实际提交它。任何错误都会出现在那里,供您捕捉和处理。

Pyramid 文档的教程中描述了会话的基本设置:

http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/tutorials/wiki2/basiclayout.html

更新:我意识到我回答了你关于在 Pyramid 中使用事务管理器的问题,你已经成功地使用了它。我认为答案也清楚地解释了发生了什么ZopeTransactionExtension,但是,您只需要确认提交事务即可。您最好在脚本中简单地使用一个事务,您可以通过

import transaction

with transaction.manager:
    # do tons of database stuff

现在,如果发生异常,事务将被中止,否则将被提交。

于 2012-10-25T15:00:46.883 回答
0

如果我没看错的话,我想我知道发生了什么......

金字塔/zope 事务管理器的缺点是它们全有或全无——由于它们的实现方式,您不能既使用它们又调用 commit()。我不记得确切的原因,但我曾经在与这个问题斗争了几个小时后深入研究了他们的所有代码,只是没有办法在页面内提交。

由于多种原因,我决定不在我的应用程序中使用自动交易包装。我有很多场景需要一个或多个提交,或者需要使用保存点(我使用 postgresql)。

于 2012-10-26T00:59:16.703 回答