Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/corridor/sqlalchemy-history
Library to keep track of changes in SQLAlchemy.
https://github.com/corridor/sqlalchemy-history
database hactoberfest mssql mysql oracle postgresql sqlalchemy sqlite
Last synced: 3 months ago
JSON representation
Library to keep track of changes in SQLAlchemy.
- Host: GitHub
- URL: https://github.com/corridor/sqlalchemy-history
- Owner: corridor
- License: other
- Created: 2022-10-04T06:47:02.000Z (about 2 years ago)
- Default Branch: master
- Last Pushed: 2024-08-09T16:35:59.000Z (3 months ago)
- Last Synced: 2024-08-10T05:24:28.741Z (3 months ago)
- Topics: database, hactoberfest, mssql, mysql, oracle, postgresql, sqlalchemy, sqlite
- Language: Python
- Homepage:
- Size: 3.8 MB
- Stars: 25
- Watchers: 3
- Forks: 23
- Open Issues: 17
-
Metadata Files:
- Readme: docs/README.md
- Changelog: CHANGES.rst
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
- jimsghstars - corridor/sqlalchemy-history - Library to keep track of changes in SQLAlchemy. (Python)
README
[![Coverage Status](https://coveralls.io/repos/github/corridor/sqlalchemy-history/badge.svg)](https://coveralls.io/github/corridor/sqlalchemy-history)
# SQLAlchemy-History
SQLAlchemy-history is a fork of sqlalchemy-continuum.
An auditing extension for sqlalchemy which keeps a track of the history of your sqlalchemy models## Features
- Supports sqlalchemy 2+ and python 3.7+
- 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 query syntax
- Query for changed records at given transaction
- Temporal relationship reflection. Get the relationships of an object in that point in time.## QuickStart
```sh
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```python
>>> 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.
```python
from sqlalchemy_history import make_versioned
from sqlalchemy import Column, Integer, Unicode, UnicodeText, create_engine
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import create_session, configure_mappers
make_versioned(user_cls=None)
Base = declarative_base()
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'
```## Resources
- [Documentation](https://corridor.github.io/sqlalchemy-history/)
- [Issue Tracker](http://github.com/corridor/sqlalchemy-history/issues)
- [Code](http://github.com/corridor/sqlalchemy-history/)## More information
- [http://en.wikipedia.org/wiki/Slowly_changing_dimension](http://en.wikipedia.org/wiki/Slowly_changing_dimension)
- [http://en.wikipedia.org/wiki/Change_data_capture](http://en.wikipedia.org/wiki/Change_data_capture)
- [http://en.wikipedia.org/wiki/Anchor_Modeling](http://en.wikipedia.org/wiki/Anchor_Modeling)
- [http://en.wikipedia.org/wiki/Shadow_table](http://en.wikipedia.org/wiki/Shadow_table)
- [https://wiki.postgresql.org/wiki/Audit_trigger](https://wiki.postgresql.org/wiki/Audit_trigger)
- [https://wiki.postgresql.org/wiki/Audit_trigger_91plus](https://wiki.postgresql.org/wiki/Audit_trigger_91plus)
- [http://kosalads.blogspot.fi/2014/06/implement-audit-functionality-in.html](http://kosalads.blogspot.fi/2014/06/implement-audit-functionality-in.html)
- [https://github.com/2ndQuadrant/pgaudit](https://github.com/2ndQuadrant/pgaudit)## 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 itWe found multiple libraries which has an implementation of history tracking:
1. [sqlalchemy-continuum](https://github.com/kvesteri/sqlalchemy-continuum)
- Does not support oracle, mssql
- Feature filled making it difficult to maintain all plugins/extensions
2. [flask-continuum](https://github.com/bprinty/flask-continuum)
- Thin wrapper on sqlalchemy-continuum specifically for flask
3. [postgresql-audit](https://github.com/kvesteri/postgresql-audit)
- Supports only postgres
4. [versionalchemy](https://github.com/NerdWalletOSS/versionalchemy)
- Not updated in a while
- No reverting capability, Relationship queries on history not available
5. [django-simple-history](https://github.com/jazzband/django-simple-history)
- Uses django ORM, does not support sqlalchemy
6. [sqlalchemy example versioning-objects](http://docs.sqlalchemy.org/en/latest/orm/examples.html#versioning-objects)
- Simple example to demonstrate implementation - but very minimal