Skip to main content

SQLAlchemy-History

PyPI Documentation

Ask DeepWiki

SQLAlchemy-History is a fork of SQLAlchemy-Continuum. It is an auditing extension that tracks the history of SQLAlchemy models.

Features

  • Supports SQLAlchemy 2+ and Python 3.10+
  • Tracks history for inserts, deletes, and updates
  • Does not store updates which don't change anything
  • Supports Alembic migrations
  • Can revert objects data as well as all object relations at given transaction even if the object was deleted
  • Transactions can be queried afterwards using SQLAlchemy select syntax
  • Query for changed records at given transaction
  • Temporal relationship reflection. Get the relationships of an object in that point in time.
  • Supports async SQLAlchemy

Quickstart

uv pip install sqlalchemy-history

In order to make your models versioned you need two things:

  1. Call make_versioned() before your models are defined.
  2. Add __versioned__ to all models you wish to add versioning to
>>> from sqlalchemy_history import make_versioned
>>> make_versioned(user_cls=None)
>>> class Article(Base):
...    __versioned__ = {}
...    __tablename__ = 'article'
...    id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
...    name = sa.Column(sa.Unicode(255))
...    content = sa.Column(sa.UnicodeText)
>>> article = Article(name='Some article', content='Some content')
>>> session.add(article)
>>> session.commit()
'article has now one version stored in database'
>>> article.versions[0].name
'Some article'
>>> article.name = 'Updated name'
>>> session.commit()
>>> article.versions[1].name
'Updated name'
>>> article.versions[0].revert()
'lets revert back to first version'
>>> article.name
'Some article'

For completeness, below is a working example.

from sqlalchemy_history import make_versioned
from sqlalchemy import Column, Integer, Unicode, UnicodeText, create_engine
from sqlalchemy.orm import DeclarativeBase, create_session, configure_mappers

make_versioned(user_cls=None)


class Base(DeclarativeBase):
    pass


class Article(Base):
    __versioned__ = {}
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(Unicode(255))
    content = Column(UnicodeText)


configure_mappers()
engine = create_engine("sqlite://")
Base.metadata.create_all(engine)
session = create_session(bind=engine, autocommit=False)
article = Article(name="Some article", content="Some content")
session.add(article)
session.commit()
print(article.versions[0].name)  # 'Some article'
article.name = "Updated name"
session.commit()
print(article.versions[1].name)  # 'Updated name'
article.versions[0].revert()
print(article.name)  # 'Some article'
Async working example
import asyncio

import sqlalchemy as sa
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase, configure_mappers

from sqlalchemy_history import make_versioned

make_versioned(user_cls=None, options={"support_async": True})


class Base(DeclarativeBase):
    pass


class Article(Base):
    __versioned__ = {}
    __tablename__ = "article"
    id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
    name = sa.Column(sa.Unicode(255))
    content = sa.Column(sa.UnicodeText)


async def main():
    configure_mappers()

    engine = create_async_engine("sqlite+aiosqlite://")

    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

    Session = async_sessionmaker(engine, expire_on_commit=False)

    async with Session() as session:
        article = Article(name="Some article", content="Some content")
        session.add(article)
        await session.commit()

        versions = (await session.scalars(article.versions.select())).all()
        print(versions[0].name)  # 'Some article'

        article.name = "Updated name"
        await session.commit()

        versions = (await session.scalars(article.versions.select())).all()
        print(versions[1].name)  # 'Updated name'

        versions[0].revert()
        await session.commit()
        print(article.name)  # 'Some article'

    await engine.dispose()


asyncio.run(main())

For more async querying and revert examples, see Async support.

Resources

More information

Comparison

Primary reasons to create another library:

  • Be future looking and support sqlalchemy 2.x
  • Support multiple databases (sqlite, mysql, postgres, mssql, oracle)
  • Focus on the history tracking and be as efficient as possible when doing it

We found multiple libraries which has an implementation of history tracking:

  1. sqlalchemy-continuum
    • Does not support oracle, mssql
    • Feature filled making it difficult to maintain all plugins/extensions
  2. flask-continuum
    • Thin wrapper on sqlalchemy-continuum specifically for flask
  3. postgresql-audit
    • Supports only postgres
  4. versionalchemy
    • Not updated in a while
    • No reverting capability, Relationship queries on history not available
  5. django-simple-history
    • Uses django ORM, does not support sqlalchemy
  6. sqlalchemy example versioning-objects
    • Simple example to demonstrate implementation - but very minimal

Metadata

Release files for SQLAlchemy-History 2.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for SQLAlchemy-History 2.2.1
File Size Uploaded Provenance Actions
sqlalchemy_history-2.2.1.tar.gz 226.2 kB With provenance Details for sqlalchemy_history-2.2.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for SQLAlchemy-History 2.2.1
File Interpreter ABI Platform Actions
sqlalchemy_history-2.2.1-py3-none-any.whl Python 3 none any Details for sqlalchemy_history-2.2.1-py3-none-any.whl

Total release size: 283.6 kB

Release files / sqlalchemy_history-2.2.1.tar.gz

Download URL sqlalchemy_history-2.2.1.tar.gz
Size 226.2 kB
Tags Source
SHA-256 checksum
How to use checksums
36769e226b4a551fdf93d22d2057efb3a52d56ced508f82310a2272e8246f2ce
BLAKE2b-256 checksum
How to use checksums
d3f14c088224c3aaf5feb658c8597f795901bf33c8cda2ae9e54fd60b2cd6285
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Release files / sqlalchemy_history-2.2.1-py3-none-any.whl

Download URL sqlalchemy_history-2.2.1-py3-none-any.whl
Size 57.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c150df603f687a554f50dc94e537d9affbea7c10438c9afa3846b3369a91ec14
BLAKE2b-256 checksum
How to use checksums
59d3bb5e6b53e29a8b4cd3d2c3eab34bb58cd14fb9c1d1d3f7dc1c679ccc69ec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Security for SQLAlchemy-History 2.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for sqlalchemy_history-2.2.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page