Install & Compatibility
Where this runs
tested against v6.3 · 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
build_error
glibcpy 3.10–3.920 runs
installs and imports cleanly · install 3.7s · import 0.262s · 33MB
38MB installed
● package 38MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
FileStorage
✓ from ZODB.FileStorage import FileStorage
DB
✓ from ZODB.DB import DB
transaction
✓ import transaction
Used for explicit transaction management (commit, abort).
This quickstart demonstrates how to create a basic ZODB `FileStorage`, store a `Persistent` object, modify it within a transaction, and then retrieve the persisted data. It highlights the use of `FileStorage`, `DB`, `Connection`, the `root` object (a dictionary-like container), and `transaction.manager` for managing commits and rollbacks. It also shows a common gotcha: marking mutable nested objects as changed.
import transaction
from ZODB.FileStorage import FileStorage
from ZODB.DB import DB
from persistent import Persistent
# Define a simple persistent class
class MyPersistentObject(Persistent):
def __init__(self, value):
self.value = value
self.data = {}
# Create a FileStorage (e.g., 'mydata.fs')
# For in-memory, use `DB(None)`
storage = FileStorage('mydata.fs')
db = DB(storage)
connection = db.open()
root = connection.root()
# Perform a transaction
with transaction.manager:
if 'my_app_root' not in root:
root['my_app_root'] = MyPersistentObject('initial value')
root['my_app_root'].data['first'] = 'Hello ZODB'
# Modify an object
app_root = root['my_app_root']
app_root.value = 'updated value'
app_root.data['second'] = 'Another entry'
# Manually mark as changed for mutable nested objects like dicts/lists
app_root.data._p_changed = True
print(f"Stored value: {app_root.value}")
print(f"Stored data: {app_root.data}")
# The 'with' statement automatically commits if no exception, aborts otherwise
# Re-open the database to verify persistence
db.close()
storage = FileStorage('mydata.fs') # Re-open storage
db = DB(storage)
connection = db.open()
root = connection.root()
app_root = root['my_app_root']
print(f"Retrieved value: {app_root.value}")
print(f"Retrieved data: {app_root.data}")
db.close()
storage.close()
Debug
Known issues
breakingMigrating ZODB databases from Python 2 to Python 3 requires a specific migration process, primarily due to changes in how Python handles `str` (bytes in Python 2, unicode in Python 3). Direct opening of a Python 2 `.fs` file with Python 3 ZODB is prevented by a different magic code in the file header.fixUse the `zodbupdate` tool and follow the Python 3 migration guide. This involves converting `str` objects that contain binary data into `zodbpickle.binary` and decoding text `str` objects.
affects: All versions when migrating from Python 2.x created databases to Python 3.x.
breakingZODB 6.0 dropped support for Python 2.7, 3.5, and 3.6. Earlier versions of Python 3 (like 3.7, 3.8, 3.9) have also been dropped in subsequent ZODB 6.x releases.fixEnsure your environment uses Python 3.10 or newer (ZODB 6.3 supports Python 3.10-3.14). Refer to the ZODB `CHANGES.rst` for exact Python compatibility per release.
affects: ZODB >= 6.0 (specific Python versions vary by minor release).
gotchaWhen modifying mutable Python objects (like lists or dictionaries) that are attributes of a `persistent.Persistent` object, ZODB does not automatically detect the change. The parent object needs to be explicitly marked as modified.fixAfter modifying a mutable attribute (e.g., `my_obj.my_list.append(item)` or `my_obj.my_dict['key'] = value`), explicitly mark the parent persistent object as changed: `my_obj.my_list._p_changed = True` or `my_obj._p_changed = True`.
affects: All versions.
gotchaChanges to the schema (attributes) of persistent classes are not automatically handled by ZODB. While it offers flexibility, evolving object schemas can lead to deserialization errors or unexpected behavior if not explicitly managed.fixPlan for schema evolution. For significant changes, write explicit migration scripts to transform existing objects in the database to the new schema. This is similar to database migrations in relational databases but needs to be custom-implemented.
affects: All versions.
breakingZODB 5.0 introduced significant internal changes to Multi-Version Concurrency Control (MVCC) implementation. Specifically, for storages implementing `IMVCCStorage` (like RelStorage), MVCC is no longer implemented directly within ZODB, simplifying client-server storage implementations. Additionally, `ConnectionPool.map()` was removed.fixReview applications using `RelStorage` or similar `IMVCCStorage` implementations, and update code that relied on `ConnectionPool.map()` (e.g., iterate `ConnectionPool` directly).
affects: ZODB >= 5.0.
Errors
Common errors & fixes
ConflictError: database conflict error
Multiple concurrent transactions attempt to write to the same object in the database, leading to a conflict that ZODB's MVCC cannot resolve without a retry.
fixImplement retry logic for transactions (ZODB often retries automatically up to three times but manual handling might be needed for complex scenarios) or redesign your application to reduce contention on heavily accessed objects, possibly by partitioning data or using specialized storages like ZEO for distributed access.
TypeError: can't concat str to bytes
Occurs during Python 3 migration, typically when a ZODB database created under Python 2 is opened with Python 3, and `str` objects (which were bytes in Python 2) are encountered where unicode strings are expected.
fixRun the `zodbupdate` migration tool on your Python 2 ZODB database *before* attempting to open it with Python 3. This tool correctly converts pickled `str` objects to either `bytes` or `str` based on their content.
AttributeError: 'module' object has no attribute 'ClassName' or 'cannot unpickle object'
A persistent object was stored in ZODB, but the Python class definition or its module path has since changed, moved, or been removed. The pickling machinery cannot find or load the necessary class to deserialize the object.
fixEnsure that all code (modules and classes) referenced by objects in the ZODB is present and importable in the Python environment. If a class or module was renamed or moved, provide 'migration hooks' or 'redirects' in your code to allow the old pickle path to map to the new class location. This often involves creating dummy classes or using `sys.modules` manipulation to intercept old import paths during deserialization.
Upgrade
Version history
6.3latest on PyPI · released Apr 14, 2026
Audit
Dependencies
persistentrequiredCore library for persistent objects, required by ZODB.
BTreesrequiredProvides highly optimized persistent B-Tree data structures, often used for indexing in ZODB.
ZConfigrequiredUsed for configuration management, especially for complex ZODB setups.
transactionrequiredManages ZODB's ACID transactions (commit, abort, begin).
zc.lockfilerequiredProvides file-based locking mechanisms for concurrent access control.
zope.interfacerequiredProvides Zope's interface definition and implementation system.
zodbpicklerequiredOptimized pickling (serialization) for ZODB objects.