Install & Compatibility
Where this runs
tested against v4.1 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.920 runs
installs and imports cleanly · install 0.0s · import 0.931s · 45.8MB
glibcpy 3.10–3.920 runs
installs and imports cleanly · install 3.8s · import 0.859s · 44MB
45MB installed
● package 45MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
register
✓ from zope.sqlalchemy import register
This is the current and recommended way to integrate a SQLAlchemy session factory with Zope transactions.
ZopeTransactionExtension
✓ from zope.sqlalchemy import ZopeTransactionEvents
✗ from zope.sqlalchemy import ZopeTransactionExtension
In `zope.sqlalchemy` v1.2, `ZopeTransactionExtension` was renamed to `ZopeTransactionEvents`. The `register()` function is now the preferred way to integrate, replacing the `extension` argument in `sessionmaker`.
This quickstart demonstrates how to set up `zope.sqlalchemy` with a SQLAlchemy engine and a scoped session. It defines a simple `User` model, creates an in-memory SQLite database, registers the `DBSession` factory, and then uses a Zope transaction to add and retrieve data. Note the explicit `transaction.commit()` and `transaction.abort()`, and `DBSession.remove()` after each operation to properly manage the session lifecycle.
import os
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, scoped_session, declarative_base
from zope.sqlalchemy import register
import transaction
# 1. Define a simple SQLAlchemy model
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50), nullable=False)
def __repr__(self):
return f"<User(id={self.id}, name='{self.name}')>"
# 2. Create an engine (using an in-memory SQLite for example)
# Use an environment variable for a real database DSN, e.g., POSTGRES_DSN='postgresql://user:pass@host/db'
database_url = os.environ.get('TEST_DSN', 'sqlite:///:memory:')
engine = create_engine(database_url)
# 3. Create tables (only if they don't exist)
Base.metadata.create_all(engine)
# 4. Create a SQLAlchemy session factory and scope it
# Scoped sessions are important for web applications or multi-threaded environments
Session = sessionmaker(bind=engine)
DBSession = scoped_session(Session)
# 5. Register the scoped session factory with zope.sqlalchemy
# This integrates its commits/rollbacks with Zope transactions
register(DBSession)
# --- Usage Example ---
# Get a session from the factory (it's thread-local/scoped)
session = DBSession()
try:
# Add a new user
new_user = User(name='Alice')
session.add(new_user)
# The transaction manager handles committing/rolling back the session
transaction.commit()
print(f"Added user: {new_user}")
# After commit, the session might be closed/invalidated by default
# Attempting to access new_user might raise DetachedInstanceError if not reloaded
# print(new_user.name) # This might fail if session is closed.
except Exception as e:
transaction.abort()
print(f"Transaction aborted due to error: {e}")
finally:
# Ensure the session is removed from the scope, especially in web requests
DBSession.remove()
# Start a new transaction for another operation
session = DBSession()
try:
# Query users
users = session.query(User).all()
print(f"Current users: {users}")
transaction.commit()
except Exception as e:
transaction.abort()
print(f"Query transaction aborted: {e}")
finally:
DBSession.remove()
Debug
Known issues
breakingAs of `zope.sqlalchemy` v3.0, you can no longer call `session.commit()` directly within a manual nested database transaction (i.e., when using `session.begin_nested()`). You must commit the savepoint returned by `session.begin_nested()` or use it as a context manager.fixInstead of `with session.begin_nested(): session.commit()`, use `with session.begin_nested(): pass` or `savepoint = session.begin_nested(); savepoint.commit()`. The outer Zope transaction will manage the ultimate commit.
affects: 3.0+
breakingIn `zope.sqlalchemy` v1.2, `ZopeTransactionExtension` was renamed to `ZopeTransactionEvents`. The recommended integration pattern also shifted from passing an `extension` argument to `sessionmaker` to using the `register()` function on the session factory.fixUpdate imports from `from zope.sqlalchemy import ZopeTransactionExtension` to `from zope.sqlalchemy import register`. Change session factory creation from `scoped_session(sessionmaker(extension=ZopeTransactionExtension(), **options))` to `DBSession = scoped_session(sessionmaker(**options)); register(DBSession)`.
affects: 1.2+
gotchaBy default, `zope.sqlalchemy` often closes sessions after `transaction.commit()`. Accessing ORM-managed objects after a commit without reloading them can lead to `DetachedInstanceError`.fixIf you need to access objects after a commit, either reload them (`session.query(MyObject).get(obj.id)`) or configure `zope.sqlalchemy.register` with `keep_session=True` for long-lasting sessions (though this can have other implications).
affects: All versions
gotchaWhen performing direct SQL write operations (not through the ORM), `zope.sqlalchemy` cannot automatically detect that the session has changed. This can lead to uncommitted changes if `transaction.commit()` is called without the session being marked as 'changed'.fixManually mark the session as changed using `zope.sqlalchemy.mark_changed(session)` after direct SQL write operations.
affects: All versions
gotchaSQLAlchemy versions 2.0.32 through 2.0.35 are known to cause deadlocks when running tests on Python 3.11+. These versions are explicitly excluded from `zope.sqlalchemy`'s supported versions.fixEnsure you are using a version of SQLAlchemy outside this problematic range (e.g., <2.0.32 or >2.0.35) when working with Python 3.11+ and `zope.sqlalchemy`.
affects: 2.0.32 - 2.0.35 of SQLAlchemy with Python 3.11+
Errors
Common errors & fixes
sqlalchemy.orm.exc.DetachedInstanceError: Instance <User at 0x...> is not bound to a Session; attribute refresh operation cannot proceed (for example, due to a delete or rollback) or validation is pending
Attempting to access an ORM object after the session it was loaded from has been closed or invalidated, typically after a `transaction.commit()` or `transaction.abort()`.
fixReload the object within a new session context or configure `zope.sqlalchemy.register` with `keep_session=True` (use with caution in web contexts). Alternatively, only access object attributes that were loaded before the session was detached.
ImportError: cannot import name 'ZopeTransactionExtension' from 'zope.sqlalchemy'
`ZopeTransactionExtension` was renamed to `ZopeTransactionEvents` in `zope.sqlalchemy` v1.2, and direct use of `extension` in `sessionmaker` is deprecated in favor of `register()`.
fixUpdate your code to use `from zope.sqlalchemy import register` and then call `register(DBSession)` after creating your `scoped_session` factory. If you *must* use the class directly for compatibility, import `ZopeTransactionEvents` instead.
Connection busy issue / The transaction is inactive due to a rollback in a subtransaction
Can occur in multi-threaded environments or complex transaction scenarios (e.g., with `Products.SQLAlchemyDA` or intricate nested transactions) where database connections or sessions are not properly isolated or released, leading to contention or premature invalidation.
fixEnsure proper session scoping, especially `scoped_session` and `DBSession.remove()` at the end of each request/thread. Review transaction logic for any manual `session.commit()` calls within implicitly managed Zope transactions. Consider database pooling settings. For `session.merge()` issues, explicitly call `session.expire_all()` or configure `register(DBSession, initial_state='changed')` to ensure state is reloaded.
sqlalchemy.exc.IntegrityError: (IntegrityError) NOT NULL constraint failed: user.name [SQL: INSERT INTO user DEFAULT VALUES]
This is a standard SQLAlchemy integrity error, but sometimes in Zope/Pyramid contexts, the traceback or error handling can be obscured if the transaction manager isn't set up to propagate exceptions cleanly.
fixEnsure your SQLAlchemy operations are wrapped in `try...except` blocks that catch `IntegrityError` (and other `sqlalchemy.exc` errors) and explicitly call `transaction.abort()` to roll back the Zope transaction. Verify the data being committed adheres to database constraints.
Upgrade
Version history
4.1latest on PyPI · released Nov 25, 2025
Audit
Dependencies
SQLAlchemyrequiredProvides the ORM and database connectivity; zope.sqlalchemy integrates its sessions with transactions. Requires SQLAlchemy >= 0.9, >= 1.1, and 2.0+ for recent versions.
transactionrequiredProvides the Zope transaction management API that zope.sqlalchemy integrates with.