Skip to content

audit_trail.mixin

The Audited mixin for audited ORM models.

Mixing Audited into a mapped class, when its mappers are configured:

  • validates the column policies and snapshot_on_load of __audit__;
  • turns on active_history for every audited column, so assigning to an expired attribute loads its old value at assignment time rather than leaving the old value unknown at flush time;
  • deep-copies the snapshot_on_load columns whenever the instance is loaded or refreshed, so an in-place change still has an old value.

Audited

Mixin marking a mapped class as audited.

Options go in __audit__; per-column policies in mapped_column(info={"audit": ...})::

class Tenant(Base, Audited):
    __tablename__ = "tenant"
    id: Mapped[int] = mapped_column(primary_key=True)
    hook_secret: Mapped[str] = mapped_column(info={"audit": "redact"})

    __audit__ = AuditOptions(label=lambda obj: obj.name)

Attributes:

Name Type Description
__audit__ AuditOptions

The model's audit options.

refresh_snapshot

refresh_snapshot(obj: object) -> None

Replace the snapshot_on_load copies with the instance's current values.

The audit listener calls this in after_flush for new and updated instances, after it has computed their changes: the flushed values are the new committed state, and the next in-place change must be compared with them. It cannot be done by the mixin itself: the mapper's after_insert/after_update events fire during the flush, before after_flush, and would overwrite the old value before the change set reads it.

Does nothing for a model without snapshot_on_load.

Parameters:

Name Type Description Default
obj object

A mapped instance.

required
Source code in audit_trail/mixin.py
def refresh_snapshot(obj: object) -> None:
    """Replace the ``snapshot_on_load`` copies with the instance's current values.

    The audit listener calls this in ``after_flush`` for new and updated
    instances, after it has computed their changes: the flushed values are the
    new committed state, and the next in-place change must be compared with
    them. It cannot be done by the mixin itself: the mapper's
    ``after_insert``/``after_update`` events fire during the flush, before
    ``after_flush``, and would overwrite the old value before the change set
    reads it.

    Does nothing for a model without ``snapshot_on_load``.

    Args:
        obj: A mapped instance.
    """
    _take_snapshot(instance_state(obj), None)