Install & Compatibility
Where this runs
tested against v1.7.0 · 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.95 runs
installs and imports cleanly · install 0.0s · import 0.986s · 43.5MB
glibcpy 3.10–3.95 runs
installs and imports cleanly · install 3.3s · import 0.886s · 42MB
42MB installed
● package 42MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
make_versioned
✓ from sqlalchemy_continuum import make_versioned
Required to be called before model definitions to enable versioning.
version_class
✓ from sqlalchemy_continuum import version_class
Used to get the dynamically generated version class for a given model.
parent_class
✓ from sqlalchemy_continuum import parent_class
Used to get the original parent class from a version class.
This quickstart demonstrates how to enable versioning for a SQLAlchemy model. It involves calling `make_versioned()` before model definitions, adding `__versioned__ = {}` to desired models, and then calling `configure_mappers()` after all models are defined. It then shows how to create, update, and retrieve historical versions of an object, including reverting to a previous state.
import sqlalchemy as sa
from sqlalchemy import create_engine, Column, Integer, Unicode, UnicodeText
from sqlalchemy.orm import sessionmaker, declarative_base, configure_mappers
from sqlalchemy_continuum import make_versioned, version_class
# Call make_versioned() before defining models
make_versioned(user_cls=None)
Base = declarative_base()
class Article(Base):
__tablename__ = 'article'
__versioned__ = {}
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(Unicode(255))
content = Column(UnicodeText)
def __repr__(self):
return f"Article(id={self.id}, name='{self.name}')"
# After defining all models, call configure_mappers
configure_mappers()
# Setup database and session
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
# Create an article
article = Article(name='Initial Name', content='Initial Content')
session.add(article)
session.commit()
print(f"Created: {article}")
# Update the article
article.name = 'Updated Name'
session.commit()
print(f"Updated: {article}")
# Access versions
ArticleVersion = version_class(Article)
versions = session.query(ArticleVersion).filter_by(id=article.id).order_by(ArticleVersion.transaction_id).all()
print(f"\nAll versions for Article {article.id}:")
for v in versions:
print(f"- Version (tx_id={v.transaction_id}): name='{v.name}'")
# Revert to the first version
if versions:
first_version = versions[0]
first_version.revert()
session.commit()
print(f"\nReverted to first version. Current article name: {article.name}")
session.close()
Debug
Known issues
breakingSQLAlchemy-Continuum versions 1.5.0 and later have dropped support for Python 3.8. Users on Python 3.8 must use an older version of the library (e.g., <1.5.0).fixUpgrade Python to 3.9+ or pin `sqlalchemy-continuum` to `<1.5.0`.
affects: >=1.5.0
breakingSQLAlchemy-Continuum versions 1.4.0 and higher exhibit incompatibility with SQLAlchemy versions less than 2.0 due to changes in SQLAlchemy's `Connection.execute` API. This can lead to `AttributeError: 'str' object has no attribute 'is_insert'`.fixEnsure `SQLAlchemy` is version `2.0` or higher when using `sqlalchemy-continuum>=1.4.0`. Alternatively, pin `sqlalchemy-continuum` to `<1.4.0` if `SQLAlchemy<2.0` is required.
affects: >=1.4.0
gotchaTracking changes in many-to-many relationships requires explicitly defining the association table as a SQLAlchemy model and marking *that model* with `__versioned__ = {}`. Otherwise, only changes to the primary entities, not the relationship itself, will be recorded in the `changeset`.fixFor many-to-many relationships, create an explicit association object model (if you don't already have one) and add `__versioned__ = {}` to it. This allows `sqlalchemy-continuum` to create a version table for the association, tracking changes to the links between entities. affects: All
gotchaFor PostgreSQL native versioning, after making schema changes (e.g., adding new columns) to a versioned table, the database trigger might become outdated.fixCall `sqlalchemy_continuum.dialects.postgresql.sync_trigger(connection, 'table_name_version')` to update the version trigger after schema modifications to ensure correct versioning behavior.
affects: All (with native PostgreSQL versioning)
gotchaInefficient querying of version history can lead to N+1 query problems, especially when traversing `.previous`/`.next` versions or relationships on version objects.fixUtilize efficient querying methods like `version_at(session, primary_keys, transaction_id=...)` for specific point-in-time lookups, and `all_versions(link=True)` for batch fetching. Also, consider the `validity` strategy and ensure composite indexes on `(entity_pk, transaction_id DESC)` are active. Avoid excessive relationship traversal on version objects if possible.
affects: All
Errors
Common errors & fixes
AttributeError: 'MyModel' object has no attribute 'versions'
SQLAlchemy-Continuum's versioning was not properly initialized via `make_versioned()` before models were mapped, preventing the addition of versioning attributes like `versions`.
fixCall `from sqlalchemy_continuum import make_versioned; make_versioned(metadata=Base.metadata)` (or simply `make_versioned()`) at the very beginning of your application setup, typically before defining or importing your SQLAlchemy declarative models.
ModuleNotFoundError: No module named 'sqlalchemy_continuum'
The `sqlalchemy-continuum` package is not installed in the active Python environment.
fixInstall the package using pip: `pip install sqlalchemy-continuum`.
NameError: name 'make_versioned' is not defined
The `make_versioned` function was used without being imported from the `sqlalchemy_continuum` package.
fixAdd `from sqlalchemy_continuum import make_versioned` to your Python file before using the function.
sqlalchemy.exc.OperationalError: (psycopg2.errors.UndefinedTable) relation "my_table_version" does not exist
The version tables (e.g., `my_table_version`) required by `sqlalchemy-continuum` have not been created in the database, likely due to forgotten Alembic migrations or an incomplete setup.
fixEnsure `make_versioned()` is correctly called in your application, then generate and apply Alembic migrations to create the version tables (e.g., `alembic revision --autogenerate -m "Add version tables"`, then `alembic upgrade head`).
Upgrade
Version history
1.7.0latest on PyPI · released Jul 3, 2026
Audit
Dependencies
SQLAlchemyrequiredCore ORM library; version 1.4.0+ of SQLAlchemy-Continuum requires SQLAlchemy>=2.0 for full compatibility.
SQLAlchemy-UtilsrequiredProvides utility functions used by SQLAlchemy-Continuum.