将sqlalchemy集成到zope 3中
z3c.zalchem的Python项目详细描述
sqlalchemy和zope 3
“z3c.zalchemy”将对象关系映射器sqlalchemy集成到zope 3中 因为sqlos集成了sqlobject。
zalchemy尽量不干扰标准的sqlalchemy 用法。zalchemy的主要部分是sqlalchemy的集成 将事务转换为zope事务。这可以通过使用数据管理器来解决 它为每个新创建的线程加入zope事务。
Zalchemy使用来自Zope的两阶段提交系统。
这就是在zope中使用两阶段提交的方式。
- tpc_begin(txn)
- commit(txn)
- tpc_vote(txn)
- tpc_finish(txn)
- commit执行session.flush(),它实际执行所有SQL语句。
- tpc_finish()在sqlalchemy事务中执行transaction.commit()
- tpc_abort()在sqlalchemy事务中执行transaction.rollback()
如果提交失败或另一个数据管理器失败,则数据未提交到 数据库。
重要
zope使用事务模块处理事务。Zalchemy插入 这个机制使用自己的数据管理器来使用zope的事务模块。
zalchemy提供方法z3c.zalchemy.getsession来获取sqlalchemy 会话对象。此方法确保会话连接到zope的 交易。
永远不要直接从sqlalchemy获取会话!
永远不要存储会话的实例也很重要。总是直接的 使用z3c.zalchemy.getsession。这是必要的,因为你不知道什么时候 交易已提交。提交总是使当前会话无效。 对getsession的新调用确保创建了新会话。
zalchemy类实现
与zope一起使用sqlalchemy没有区别。
Zalchemy提供了一种将表连接到数据库(引擎)的透明方法。
sqlalchemy引擎表示为实用程序:
>>> from z3c.zalchemy.datamanager import AlchemyEngineUtility >>> engineUtility = AlchemyEngineUtility( ... 'database', ... 'sqlite:///%s'%dbFilename, ... echo=False, ... )
我们将表创建为普通的sqlalchemy表。重要的是 这里,必须使用来自zalchemy的元数据。请注意你 需要调用z3c.zalchemy.metadata。
>>> import sqlalchemy >>> import z3c.zalchemy >>> table3 = sqlalchemy.Table( ... 'table3', ... z3c.zalchemy.metadata(), ... sqlalchemy.Column('id', sqlalchemy.Integer, ... sqlalchemy.Sequence('atable_id'), primary_key=True), ... sqlalchemy.Column('value', sqlalchemy.Integer), ... )
定义一个简单的类,该类稍后将用于映射到数据库表。
>>> class A(object): ... pass
现在我们把桌子映射到我们班上。
>>> sqlalchemy.mapper(A, table3) is not None True
为了让zalchemy完成它的工作,我们需要注册我们的数据库实用程序。
>>> from z3c.zalchemy.interfaces import IAlchemyEngineUtility >>> from zope.component import provideUtility >>> provideUtility(engineUtility, IAlchemyEngineUtility)
可以在不打开事务或会话的情况下创建表。 如果没有创建会话,则表的创建将推迟到下一个 调用zalchemy.getsession。
>>> z3c.zalchemy.createTable('table3', '')
Zalchemy自动协调Zope的事务管理器 sqlalchemy的会话。所有映射类都自动与 线程本地会话,它自动连接到一个特殊的 与Zope的事务协调的数据管理器。
>>> a = A() >>> a.value = 1
提交事务将自动触发刷新并清除 会议。
>>> import transaction >>> transaction.commit()
现在让我们尝试在一个新事务中恢复对象(我们在一个新事务中 已提交事务,因为已提交旧事务:
>>> from z3c.zalchemy.datamanager import getSession as session >>> a = session().get(A, 1) >>> a.value 1>>> transaction.commit()
多个数据库
上面的例子假设只有一个数据库。数据库 引擎已注册为未命名的实用程序。未命名的实用程序总是 新会话的默认数据库。
这会自动将每个表分配给默认引擎。
对于多个数据库,可以将表分配给引擎。
我们创建了一个新的数据库引擎:
>>> engine2Util = AlchemyEngineUtility( ... 'engine2', ... 'sqlite:///%s'%dbFilename2, ... echo=False, ... )
因为已经有一个默认引擎,我们必须为 新引擎。
>>> provideUtility(engine2Util, IAlchemyEngineUtility, name='engine2')>>> bTable = sqlalchemy.Table( ... 'bTable', ... z3c.zalchemy.metadata(), ... sqlalchemy.Column('id', sqlalchemy.Integer, ... sqlalchemy.Sequence('btable_id'), primary_key=True), ... sqlalchemy.Column('value', sqlalchemy.String), ... )>>> class B(object): ... pass >>> B.mapper = sqlalchemy.mapper(B, bTable)
将btable分配给新引擎并创建表。 这一次我们是在一个疗程内完成的。
>>> z3c.zalchemy.assignTable('bTable', 'engine2') >>> z3c.zalchemy.createTable('bTable', 'engine2')>>> b = B() >>> b.value = 'b1'>>> a = A() >>> a.value = 321>>> transaction.commit()>>> a = session().get(A, 1) >>> b = session().get(B, 1) >>> str(b.value) 'b1'>>> transaction.commit()
也可以将类分配给数据库:
>>> class Aa(object): ... pass >>> sqlalchemy.mapper(Aa, table3) is not None True
现在我们可以将类分配给引擎:
>>> z3c.zalchemy.assignClass(Aa, 'engine2')
现在的问题是我们的发动机里没有桌子2英寸。 我们可以使用一个附加参数来创建表:
>>> z3c.zalchemy.createTable('table3', 'engine2')>>> aa = Aa() >>> aa.value = 100>>> transaction.commit()
不同数据库中同名的表
如果我们有两个数据库,其中包含同名但带有 不同的结构我们需要把一个表明确地分配给一个数据库。这个 必须通过请求特定引擎的元数据来完成。
>>> b2Table = sqlalchemy.Table( ... 'bTable', ... z3c.zalchemy.metadata('b2Engine'), ... sqlalchemy.Column('id', sqlalchemy.Integer, ... sqlalchemy.Sequence('btable_id'), primary_key=True), ... sqlalchemy.Column('b2value', sqlalchemy.String), ... )
我们现在可以通过提供引擎来请求该表。
>>> z3c.zalchemy.metadata.getTable('b2Engine', 'bTable', True) Table('bTable',...
如果我们为“默认”引擎指定了一个表,那么我们可以请求 “b2engine”的“btable”,并回退到默认引擎。
>>> z3c.zalchemy.metadata.getTable('b2Engine', 'table3', True) Table('table3',...
更改
0.2.2(2011-08-05)
- Fix packaging problem: Declare namespace package ‘z3c’.
0.2.1-2007年11月13日
- Conflict detection did not work with savepoints.
0.2-2007-09-27
Added a way to register database specific adapters for conflict detection and possible re-do by the publisher.
Using the threadlocal strategy of sqlalchemy instead of doing that ourselves.
Added support for optimistic savepoints. This can be used, similarly to what happens with ZODB, to flush intermediary work without committing.
Provide a tighter integration with Zope’s transaction mechanism. Sessions are now automatically associated with new objects. We rely on SQLAlchemy’s SessionContext object which hands out a session for each thread. Your code rarely should never have to call session.save(object) now.
One incompatible change was introduced: You can not call getSession before registering an (unnamed) engine utility first. Doing so will raise a ValueError.
0.1.1-2007-06-27
- Fixed a failing test in TRANSACTION.txt where an exception demonstrated a string being returned but it was a unicode string.
0.1-从未释放
- This was supposed to be the first release, but we missed a broken test. See 0.1.1