SQLAlchemy integration#
SQLAlchemy integration offers easy way of using PostgreSQL-Audit with SQLAlchemy ORM. It has the following features:
Automatically marks all declarative classes, which have
__versioned__
class property defined, as versioned.Attaches
after_create()
DDL listeners that create versioning triggers for all versioned tables.Provides
Activity
model for easy ORM level access of activities.
from postgresql_audit import versioning_manager
versioning_manager.init(Base)
class Article(Base):
__tablename__ = 'article'
__versioned__ = {}
id = Column(Integer, primary_key=True)
name = Column(String)
article = Article(name='Some article')
session.add(article)
session.commit()
Excluding columns#
You can easily exclude columns from being versioned by adding them as a list to
the 'exclude'
key of __versioned__
dict:
class Article(Base):
__tablename__ = 'article'
__versioned__ = {'exclude': 'created_at'}
id = Column(Integer, primary_key=True)
name = Column(String)
created_at = Column(DateTime)
Versioning many-to-many tables#
Versioning Table
objects is easy. Just call
audit_table
method with the desired table:
class User(Base):
__tablename__ = 'user'
__versioned__ = {}
id = Column(Integer, primary_key=True)
name = Column(String)
class Group(Base):
__tablename__ = 'group'
__versioned__ = {}
id = Column(Integer, primary_key=True)
name = Column(String)
group_user = Table(
'group_user',
Base.metadata,
Column(
'user_id',
Integer,
ForeignKey('user.id', ondelete='CASCADE'),
nullable=False,
primary_key=True
),
Column(
'group_id',
Integer,
ForeignKey('place.id', ondelete='CASCADE'),
nullable=False,
primary_key=True
)
versioning_manager.audit_table(group_user)
Tracking inserts#
Now we can check the newly created activity:
Activity = versioning_manager.activity_cls
activity = Activity.query.first()
activity.id # 1
activity.table_name # 'article'
activity.verb # 'insert'
activity.old_data # {}
activity.changed_data # {'id': '1', 'name': 'Some article'}
Tracking updates#
article.name = 'Some other article'
session.commit()
activity = Activity.query.order_by(db.desc(Activity.id)).first()
activity.id # 2
activity.table_name # 'article'
activity.verb # 'update'
activity.old_data # {'id': '1', 'name': 'Some article'}
activity.changed_data # {'name': 'Some other article'}
Tracking deletes#
session.delete(article)
session.commit()
activity = Activity.query.order_by(db.desc(Activity.id)).first()
activity.id # 3
activity.table_name # 'article'
activity.verb # 'delete'
activity.old_data # {'id': '1', 'name': 'Some other article'}
activity.changed_data # {}
Finding history of specific record#
In this example, we want to find all changes made to an Article
entity. The
query is a bit complex since we have to check old_data
and changed_data
separately. Luckily, the Activity
model has a
hybrid_property
called data
which is a
combination of these two. Hence, you can get the desired activities as follows:
activities = session.query(Activity).filter(
Activity.table_name == 'article',
Activity.data['id'].astext.cast(db.Integer) == 3
)
Temporarily disabling inserts to the activity
table#
There are cases where you might not want to track changes to your data, such as
when doing big changes to a table. In those cases, you can use the
VersioningManager.disable
context manager:
with versioning_manager.disable(session):
for i in range(1, 10000):
db.session.add(db.Product(name='Product %s' % i))
db.session.commit()