最小Zope/SQLAlchemy事务集成
项目描述
简介
本包的目的是统一现有的多种将 SQLAlchemy 与 Zope 事务管理集成的包。因此,它只提供数据管理器,并不试图定义一种配置引擎的 zopeish 方式。
对于 WSGI 应用,可以使用 repoze.tm2(Turbogears 2 和其他系统使用)实现 Zope 风格的自动事务管理。
该包还用于 pyramid_tm(Pyramid 的附加组件)Web 框架。
为了理解此包以及本 README,您需要了解 SQLAlchemy 和 Zope 事务管理器。
运行测试
此包作为 buildout 分发。使用您想要的 python 运行
$ python bootstrap.py $ ./bin/buildout
这将下载依赖包并设置测试脚本,可以使用以下命令运行
$ ./bin/test
或使用标准的 setuptools 测试命令
$ ./bin/py setup.py test
要启用与您自己的数据库的测试,请设置 TEST_DSN 环境变量为您的 sqlalchemy 数据库 dsn。可以通过设置 TEST_TWOPHASE 变量为一个非空字符串来测试两阶段提交行为。例如
$ TEST_DSN=postgres://test:test@localhost/test TEST_TWOPHASE=True bin/test
简要用法
使用会话工厂类上的 register() 函数在 Zope 事务与 SQLAlchemy 事件系统之间进行集成。
from zope.sqlalchemy import register
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
engine = sqlalchemy.create_engine("postgresql://scott:tiger@localhost/test")
DBSession = scoped_session(sessionmaker(bind=engine))
register(DBSession)
现在,实例化会话的提交和回滚将与 Zope 事务集成。
import transaction
from sqlalchemy.sql import text
session = DBSession()
result = session.execute(text("DELETE FROM objects WHERE id=:id"), {"id": 2})
row = result.fetchone()
transaction.commit()
完整示例
以下示例直接来自 SQLAlchemy 声明式文档。首先,必要的导入。
>>> from sqlalchemy import * >>> from sqlalchemy.orm import declarative_base, scoped_session, sessionmaker, relationship >>> from sqlalchemy.sql import text >>> from zope.sqlalchemy import register >>> import transaction
现在定义映射类。
>>> Base = declarative_base() >>> class User(Base): ... __tablename__ = 'test_users' ... id = Column('id', Integer, primary_key=True) ... name = Column('name', String(50)) ... addresses = relationship("Address", backref="user") >>> class Address(Base): ... __tablename__ = 'test_addresses' ... id = Column('id', Integer, primary_key=True) ... email = Column('email', String(50)) ... user_id = Column('user_id', Integer, ForeignKey('test_users.id'))
创建一个引擎并设置表。请注意,为了使此示例工作,需要 sqlite/pysqlite 的最新版本。3.4.0 似乎足够。
>>> engine = create_engine(TEST_DSN) >>> Base.metadata.create_all(engine)
现在创建会话本身。由于 Zope 是一个线程化 Web 服务器,我们必须使用作用域会话。通过使用 register 将 Zope 和 SQLAlchemy 会话绑定在一起。
>>> Session = scoped_session(sessionmaker(bind=engine, ... twophase=TEST_TWOPHASE))
调用作用域会话工厂以检索会话。您可以在事务中调用此操作任意次数,您将始终检索到相同的会话。目前数据库中没有用户。
>>> session = Session() >>> register(session) <zope.sqlalchemy.datamanager.ZopeTransactionEvents object at ...> >>> session.query(User).all() []
现在我们可以创建一个新用户并使用 Zope 的事务机制提交更改,就像 Zope 的发布者一样。
>>> session.add(User(id=1, name='bob')) >>> transaction.commit()
引擎级别的连接不在事务集成范围内。
>>> engine.connect().execute(text('SELECT * FROM test_users')).fetchall() [(1, ...'bob')]
新事务需要新的会话。让我们添加一个地址。
>>> session = Session() >>> bob = session.query(User).all()[0] >>> str(bob.name) 'bob' >>> bob.addresses [] >>> bob.addresses.append(Address(id=1, email='[email protected]')) >>> transaction.commit() >>> session = Session() >>> bob = session.query(User).all()[0] >>> bob.addresses [<Address object at ...>] >>> str(bob.addresses[0].email) '[email protected]' >>> bob.addresses[0].email = 'wrong@wrong'
要回滚事务,请使用 transaction.abort()。
>>> transaction.abort() >>> session = Session() >>> bob = session.query(User).all()[0] >>> str(bob.addresses[0].email) '[email protected]' >>> transaction.abort()
默认情况下,zope.sqlalchemy 在会话首次使用时将其置于“活动”状态。ORM 写操作会自动将会话移动到“更改”状态。这避免了不必要的数据库提交。有时需要通过 SQL 直接与数据库交互。无法猜测此类操作是读取还是写入。因此,在手动 SQL 语句写入数据库时,我们必须手动将会话标记为更改。
>>> session = Session() >>> conn = session.connection() >>> users = Base.metadata.tables['test_users'] >>> conn.execute(users.update().where(users.c.name=='bob'), {'name': 'ben'}) <sqlalchemy.engine... object at ...> >>> from zope.sqlalchemy import mark_changed >>> mark_changed(session) >>> transaction.commit() >>> session = Session() >>> str(session.query(User).all()[0].name) 'ben' >>> transaction.abort()
如果这成为问题,您可以通过注册事件并告诉它们将会话初始置于“更改”状态来解决这个问题。
>>> Session.remove() >>> register(Session, 'changed') <zope.sqlalchemy.datamanager.ZopeTransactionEvents object at ...> >>> session = Session() >>> conn = session.connection() >>> conn.execute(users.update().where(users.c.name=='ben'), {'name': 'bob'}) <sqlalchemy.engine... object at ...> >>> transaction.commit() >>> session = Session() >>> str(session.query(User).all()[0].name) 'bob' >>> transaction.abort()
mark_changed 函数接受一个 kwarg,用于 keep_session,默认为 False,并且不了解已注册的扩展 keep_session 配置。
如果您打算将 keep_session 设置为 True,您可以明确指定它
>>> from zope.sqlalchemy import mark_changed >>> mark_changed(session, keep_session=True) >>> transaction.commit()
您还可以使用配置的扩展来保留此参数
>>> sessionExtension = register(session, keep_session=True) >>> sessionExtension.mark_changed(session) >>> transaction.commit()
持久会话作用域
事务集成的默认行为是在提交后关闭会话。您可以尝试提交后访问对象来了解这一点
>>> bob = session.query(User).all()[0] >>> transaction.commit() >>> bob.name Traceback (most recent call last): sqlalchemy.orm.exc.DetachedInstanceError: Instance <User at ...> is not bound to a Session; attribute refresh operation cannot proceed...
为了支持需要比事务持续时间长的会话的情况(在测试套件中很有用),您可以在注册事件时指定保留会话。
>>> Session = scoped_session(sessionmaker(bind=engine, ... twophase=TEST_TWOPHASE)) >>> register(Session, keep_session=True) <zope.sqlalchemy.datamanager.ZopeTransactionEvents object at ...> >>> session = Session() >>> bob = session.query(User).all()[0] >>> bob.name = 'bobby' >>> transaction.commit() >>> bob.name 'bobby'
然后必须手动关闭会话。
>>> session.close()
开发版本
变更
3.1 (2023-09-12)
修复psycopg.errors.OperationalError.sqlstate可以None的问题。(#81)
3.0 (2023-06-01)
添加对SQLAlchemy 2.0和新psycopg v3后端的支持。(#79)
破坏性变更
不再允许在手动嵌套数据库事务(一个保存点)中调用session.commit()。如果您想在不知道transaction.savepoint()和session.begin_nested()的代码中使用保存点,则使用函数返回的保存点提交嵌套事务,即savepoint = session.begin_nested(); savepoint.commit(),或者将其用作上下文管理器,即with session.begin_nested():。(详情见#79)
2.0 (2023-02-06)
停止支持Python 2.7、3.5、3.6。
停止支持SQLAlchemy < 1.1(#65)
添加对Python 3.10、3.11的支持。
1.6 (2021-09-06)
添加对Python 2.7在SQLAlchemy 1.4上的支持。(#71)
1.5 (2021-07-14)
1.4 (2021-04-26)
1.3 (2020-02-17)
.datamanager.register()现在返回用于注册事件的ZopeTransactionEvents实例。这允许之后更改其参数。(#40)
初步支持Python 3.9a3。
1.2 (2019-10-17)
破坏性变更
停止支持Python 3.4。
添加对Python 3.7和3.8的支持。
修复事件系统的弃用警告。我们已经在一般情况下使用了它,但仍在某些地方利用了旧的扩展机制。(#31)
为了使事情更清晰,我们将ZopeTransactionExtension类重命名为ZopeTransactionEvents。使用‘register’版本的现有代码保持兼容。
从1.1升级
您的旧代码如下
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), **options))
变为
from zope.sqlalchemy import register
DBSession = scoped_session(sessionmaker(**options))
register(DBSession)
1.1 (2019-01-03)
添加使用pymysql的MySQL支持。
1.0 (2018-01-31)
添加对Python 3.4至3.6的支持。
支持SQLAlchemy 1.2。
停止支持Python 2.6、3.2和3.3。
停止支持transaction < 1.6.0。
修复在罕见情况下提交事务时可能导致SQLAlchemy会话未提交的隐患。(#23)
0.7.7 (2016-06-23)
支持SQLAlchemy 1.1。(#15)
0.7.6 (2015-03-20)
使register中的版本检查与预发布版本兼容。
0.7.5 (2014-06-17)
确保在不需要数据库提交的情况下,事务提交后映射对象已过期。(#8)
0.7.4 (2014-01-06)
0.7.3 (2013-09-25)
防止在事务加入失败时,Session 对象进入“卡住”状态。对于可重用的线程作用域会话,这可能导致需要重新启动服务器的持久性错误。(#2)
0.7.2 (2013-02-19)
使会话的生存期可配置。在设置 SA 扩展时指定 keep_session=True。
Python 3.3 兼容性。
0.7.1 (2012-05-19)
使用 @implementer 作为类装饰器,而不是在类作用域中使用 implements(),以与 zope.interface 4.0 兼容。这需要 zope.interface >= 3.6.0。
0.7 (2011-12-06)
Python 3.2 兼容性。
0.6.1 (2011-01-08)
更新 datamanager.mark_changed 以处理尚未记录(ORM)查询的会话。
0.6 (2010-07-24)
实现 should_retry 以处理 sqlalchemy.orm.exc.ConcurrentModificationError 和来自 PostgreSQL 和 Oracle 的序列化错误。(指定 transaction>=1.1 以使用此功能。)
包含许可文件。
将 transaction_manager 属性添加到数据管理器,以符合 IDataManager 接口。
0.5 (2010-06-07)
在 savepoint 操作中移除冗余的 session.flush() / session.clear()。这些在 SQLAlchemy 0.4.x 中是必需的。
支持 SQLAlchemy 0.6.x。需要 SQLAlchemy >= 0.5.1。
添加运行 python setup.py test 的支持。
明确拉入 pysqlite 作为测试依赖。
在测试设置中设置 sqlalchemy mappers 并在拆卸中清除它们。这使测试更稳健,并在之后清理全局状态。当同一运行中的其他测试调用 clear_mappers 时,这会导致测试失败。
0.4 (2009-01-20)
已修复的漏洞
只有在已提交的情况下,才在 tpc_abort 中引发错误。
在解除引用会话之前(即在所有工作都已成功完成之前),从 SESSION_STATE 中移除会话 ID。这修复了在事务提交失败但 SESSION_STATE 已清除的情况。在这种情况下,由于 abort 总是会引发错误,事务会卡住。这发生在 PostgreSQL 上,其中使用了无效的 SQL 并捕获了错误。
在 tpc_begin 中无条件调用 session.flush()。
修改 session.commit() 上的错误消息,使其对非 zope 用户更友好。
功能变更
支持 SQLAlchemy 0.5.1 的批量更新和删除。
0.3 (2008-07-29)
已修复的漏洞
添加到会话中的新对象不会导致事务加入,因此除非访问了数据库,否则在事务结束时不会提交。现在需要 SQLAlchemy 0.4.7 或 0.5beta3。
功能变更
为了正确性和与 ZODB 的一致性,将函数“invalidate”重命名为“mark_changed”,将状态“invalidated”重命名为“changed”。
0.2 (2008-06-28)
功能变更
更新以支持 SQLAlchemy 0.5。(0.4.6 仍然支持。)
0.1 (2008-05-15)
首次公开发布。
项目详情
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。