Introduction¶
Why?¶
SQLAlchemy already has a versioning extension. This extension however is very limited. It does not support versioning entire transactions.
Hibernate for Java has Envers, which had nice features but lacks a nice API. Ruby on Rails has papertrail, which has very nice API but lacks the efficiency and feature set of Envers.
As a Python/SQLAlchemy enthusiast I wanted to create a database versioning tool for Python with all the features of Envers and with as intuitive API as papertrail. Also I wanted to make it fast keeping things as close to the database as possible.
Features¶
- 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 query syntax
- Querying for changed records at given transaction
- Querying for versions of entity that modified given property
- Querying for transactions, at which entities of a given class changed
- Version models give access to parent objects relations at any given point in time
Installation¶
Basics¶
In order to make your models versioned you need two things:
- Call make_versioned() before your models are defined.
- Add
__versioned__to all models you wish to add versioning to
import sqlalchemy as sa
from sqlalchemy_continuum 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)
# after you have defined all your models, call configure_mappers:
sa.orm.configure_mappers()
After this setup SQLAlchemy-Continuum does the following things:
- It creates ArticleVersion model (with table name
article_version) that acts as version history for Article model - Creates a Transaction model for transaction tracking (a TransactionChanges model is only created if you use the TransactionChangesPlugin)
- Adds couple of listeners so that each Article object insert, update and delete gets recorded
When the models have been configured either by calling configure_mappers() or by accessing some of them the first time, the following things become available:
from sqlalchemy_continuum import version_class, parent_class
version_class(Article) # ArticleVersion class
parent_class(version_class(Article)) # Article class
Versions and transactions¶
At the end of each transaction SQLAlchemy-Continuum gathers all changes together and creates version objects for each changed versioned entity. Continuum also creates one Transaction entity per transaction for transaction tracking. (If the TransactionChangesPlugin is enabled, N TransactionChanges entities are also created per transaction, where N is the number of affected classes per transaction.)