django-history-triggers is a Django application for installing database triggers
that automatically record inserts, updates, and deletes to model tables.
- Django 4.2+
- PostgreSQL or SQLite database
Warning: there is a bug in SQLite 3.45.1 that causes JSON changes to be stored as strings. Unfortunately, this is the version that ships with Ubuntu 24.04.
pip install django-history-triggers
- Add
historyto your INSTALLED_APPS setting and migrate your database. - Add
history.middleware.HistoryMiddlewareto the end of yourMIDDLEWAREsetting. - Run
manage.py triggers enableto install the trigger functions, ormanage.py triggers disableto uninstall them. Neither will clear existing history data -- add a--clearoption to do that.
HISTORY_MODEL(default:"history.ObjectHistory")HISTORY_IGNORE_APPS(default:["admin", "contenttypes", "sessions"])HISTORY_IGNORE_MODELS(default[]- should be lowercaseapp_label.model_name)HISTORY_MIDDLEWARE_IGNORE(default:[])HISTORY_FILTER(default:"history.utils.default_filter")HISTORY_REQUEST_CONTEXT(default:"history.utils.get_request_context")HISTORY_ADMIN_ENABLED(default:True)HISTORY_INCLUDE_UNMANAGED(default:True)
History is recorded within "sessions" that you can manage manually, either outside of a web request context, or in place of or in addition to the included middleware. The easiest way to manage a history session is via a context manager:
from history import get_backend
def api_view(request):
# You can pass extra fields to be stored for all history within a session.
with get_backend().session(user=request.user, path=request.path):
# All history inside here will have the same session_id and session_date.
...Starting in 3.4.2, you can also "pause" history recording within a session:
with get_backend().session() as session:
Model.objects.create(name="This history is recorded")
with session.paused():
Model.objects.create(name="This history is NOT recorded")
Model.objects.create(name="This history is also recorded")The default history.ObjectHistory model is swappable by changing the HISTORY_MODEL
setting. If you need to define your own object history model (usually for tracking
custom fields or non-standard user info), be sure to inherit from
history.models.AbstractObjectHistory. If at all possible, do this early on to avoid
problems with migrations when changing HISTORY_MODEL after the initial migration.
The HISTORY_FILTER setting allows you to fully customize which fields (or even whole
models) should be included in or excluded from history. It is implemented as a callable
that takes three parameters:
- The
django.db.models.Modelclass being filtered - The
django.db.models.fields.Fieldinstance in question - The
history.models.TriggerTypebeing created
The filter should return True if the field should be included, and False if it
should be excluded. The default implementation (history.utils.default_filter) simply
includes any field except BinaryFields:
def default_filter(model, field, trigger_type):
return not isinstance(field, models.BinaryField)Returning False for all fields of any given model has the effect of not tracking
history for that model:
def filter_sensitive(model, field, trigger_type):
return not issubclass(model, SensitiveDataModel)Similarly, if you (for example) only wanted to record history for UPDATE statements:
def updates_only(model, field, trigger_type):
return trigger_type == TriggerType.UPDATEBy default django-history-triggers does not override any of Django's management
commands that may perform database operations, such as loaddata or migrate. If you
need to run these commands with history triggers enabled, you can include the following
apps in your INSTALLED_APPS setting:
history.contrib.loaddatahistory.contrib.migrate
The HISTORY_LOADDATA_CONTEXT and HISTORY_MIGRATE_CONTEXT settings control the
history session context for the respective command, for example:
HISTORY_MIGRATE_CONTEXT = {"user": "system"}