{"id":5067,"library":"sqlalchemy-continuum","title":"SQLAlchemy-Continuum","description":"SQLAlchemy-Continuum is a versioning and auditing extension for SQLAlchemy. It automatically creates versions for inserts, deletes, and updates of SQLAlchemy models, supports Alembic migrations, and allows reverting objects and their relations to previous states. The current version, 1.6.0, was released in January 2026, indicating an active development and maintenance cadence with recent updates for Python 3.13/3.14 and SQLAlchemy 2.0 compatibility.","status":"active","version":"1.6.0","language":"en","source_language":"en","source_url":"https://github.com/kvesteri/sqlalchemy-continuum","tags":["SQLAlchemy","ORM","versioning","auditing","database","history","data-management"],"install":[{"cmd":"pip install SQLAlchemy-Continuum","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Core ORM library; version 1.4.0+ of SQLAlchemy-Continuum requires SQLAlchemy>=2.0 for full compatibility.","package":"SQLAlchemy"},{"reason":"Provides utility functions used by SQLAlchemy-Continuum.","package":"SQLAlchemy-Utils","optional":false}],"imports":[{"note":"Required to be called before model definitions to enable versioning.","symbol":"make_versioned","correct":"from sqlalchemy_continuum import make_versioned"},{"note":"Used to get the dynamically generated version class for a given model.","symbol":"version_class","correct":"from sqlalchemy_continuum import version_class"},{"note":"Used to get the original parent class from a version class.","symbol":"parent_class","correct":"from sqlalchemy_continuum import parent_class"}],"quickstart":{"code":"import sqlalchemy as sa\nfrom sqlalchemy import create_engine, Column, Integer, Unicode, UnicodeText\nfrom sqlalchemy.orm import sessionmaker, declarative_base, configure_mappers\nfrom sqlalchemy_continuum import make_versioned, version_class\n\n# Call make_versioned() before defining models\nmake_versioned(user_cls=None)\n\nBase = declarative_base()\n\nclass Article(Base):\n    __tablename__ = 'article'\n    __versioned__ = {}\n    id = Column(Integer, primary_key=True, autoincrement=True)\n    name = Column(Unicode(255))\n    content = Column(UnicodeText)\n\n    def __repr__(self):\n        return f\"Article(id={self.id}, name='{self.name}')\"\n\n# After defining all models, call configure_mappers\nconfigure_mappers()\n\n# Setup database and session\nengine = create_engine('sqlite:///:memory:')\nBase.metadata.create_all(engine)\nSession = sessionmaker(bind=engine)\nsession = Session()\n\n# Create an article\narticle = Article(name='Initial Name', content='Initial Content')\nsession.add(article)\nsession.commit()\nprint(f\"Created: {article}\")\n\n# Update the article\narticle.name = 'Updated Name'\nsession.commit()\nprint(f\"Updated: {article}\")\n\n# Access versions\nArticleVersion = version_class(Article)\nversions = session.query(ArticleVersion).filter_by(id=article.id).order_by(ArticleVersion.transaction_id).all()\n\nprint(f\"\\nAll versions for Article {article.id}:\")\nfor v in versions:\n    print(f\"- Version (tx_id={v.transaction_id}): name='{v.name}'\")\n\n# Revert to the first version\nif versions:\n    first_version = versions[0]\n    first_version.revert()\n    session.commit()\n    print(f\"\\nReverted to first version. Current article name: {article.name}\")\n\nsession.close()\n","lang":"python","description":"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."},"warnings":[{"fix":"Upgrade Python to 3.9+ or pin `sqlalchemy-continuum` to `<1.5.0`.","message":"SQLAlchemy-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).","severity":"breaking","affected_versions":">=1.5.0"},{"fix":"Ensure `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.","message":"SQLAlchemy-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'`.","severity":"breaking","affected_versions":">=1.4.0"},{"fix":"For 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.","message":"Tracking 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`.","severity":"gotcha","affected_versions":"All"},{"fix":"Call `sqlalchemy_continuum.dialects.postgresql.sync_trigger(connection, 'table_name_version')` to update the version trigger after schema modifications to ensure correct versioning behavior.","message":"For PostgreSQL native versioning, after making schema changes (e.g., adding new columns) to a versioned table, the database trigger might become outdated.","severity":"gotcha","affected_versions":"All (with native PostgreSQL versioning)"},{"fix":"Utilize 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.","message":"Inefficient querying of version history can lead to N+1 query problems, especially when traversing `.previous`/`.next` versions or relationships on version objects.","severity":"gotcha","affected_versions":"All"}],"env_vars":null,"last_verified":"2026-04-12T00:00:00.000Z","next_check":"2026-07-11T00:00:00.000Z"}