跳转到主要内容

最小Zope/SQLAlchemy事务集成

项目描述

简介

本包的目的是统一现有的多种将 SQLAlchemy 与 Zope 事务管理集成的包。因此,它只提供数据管理器,并不试图定义一种配置引擎的 zopeish 方式。

对于 WSGI 应用,可以使用 repoze.tm2(Turbogears 2 和其他系统使用)实现 Zope 风格的自动事务管理。

该包还用于 pyramid_tm(Pyramid 的附加组件)Web 框架。

为了理解此包以及本 README,您需要了解 SQLAlchemyZope 事务管理器

运行测试

此包作为 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()

开发版本

GIT版本

变更

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)

  • do_orm_execute事件上调用mark_changed,如果操作是插入、更新或删除。这仅适用于SQLAlchemy >= 1.4,因为它是引入该事件的版本。(#67

  • 修复获取事务的问题。在1.4中引入了回归。(#66

1.4 (2021-04-26)

  • mark_changedjoin_transaction方法添加到ZopeTransactionEvents。(#46

  • 减少与SQLAlchemy 1.4的弃用警告,并要求至少使用SQLAlchemy >= 0.9。(#54

  • 添加对SQLAlchemy 1.4的支持。(#58

  • 防止使用损坏的flush支持的SQLAlchemy 1.4版本。(#57

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)

  • 允许在嵌套事务中使用session.commit(),以方便集成可能不使用transaction.savepoint()的现有代码。(#1

  • 添加了新的函数 zope.sqlalchemy.register(),它替换了直接使用 ZopeTransactionExtension,以便利用更新的 SQLAlchemy 事件系统在给定的 Session 实例/类/工厂上建立检测。需要至少 SQLAlchemy 0.7。(#4

  • 修复了在事务被 flush 和其他管理者联合时,keep_session=True 不起作用的问题。(#5

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)

  • 首次公开发布。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源代码分发

zope.sqlalchemy-3.1.tar.gz (32.4 kB 查看哈希值)

上传时间 源代码

构建分发

zope.sqlalchemy-3.1-py3-none-any.whl (23.4 kB 查看哈希值)

上传时间 Python 3

支持

AWSAWS云计算和安全赞助商DatadogDatadog监控FastlyFastlyCDNGoogleGoogle下载分析MicrosoftMicrosoftPSF赞助商PingdomPingdom监控SentrySentry错误记录StatusPageStatusPage状态页面