django-simple-history
Automatically tracks every create, update, and delete on your Django models, with a full admin UI to browse, diff, and revert history.
Repository Health
Technical Analysis
django-simple-history is a Django app that transparently records the full change history of your models. Attach a HistoricalRecords() descriptor to any model and the library generates a parallel Historical<Model> table that captures a snapshot on every create, update, and delete, preserving the original model’s field types instead of collapsing changes into a generic JSON blob.
Beyond raw logging, it ships an admin integration that lets staff browse past versions and revert with one click, a diff_against() API for field-level change comparisons, and a register() function for tracking third-party models you don’t own. A middleware-backed context var can auto-populate the user responsible for each change, and the project maintains compatibility across the current Django and Python support matrix with Postgres, MySQL, and SQLite.
What You Get
- A
HistoricalRecords()model field that auto-generates a historical shadow model, table, and migration for any Django model you track SimpleHistoryAdmin, a drop-in admin class that adds a version history view with browse and one-click revertdiff_against()for structured field-level diffing between any two historical records, including many-to-many changesregister()for retroactively tracking third-party or vendor models without modifying their sourceHistoryRequestMiddlewarefor automatically attaching the requesting user to each historical record- Signals (
pre_create_historical_record,post_create_historical_record, etc.) for hooking custom logic into the history-recording lifecycle
Common Use Cases
- Adding an audit trail to admin-managed models so staff can see who changed what and when
- Building a “revert to previous version” feature for content or configuration records
- Meeting compliance/audit requirements that demand a durable, queryable change log per row
- Tracking changes on third-party app models (e.g. Django’s own User model) without forking them
- Powering timeline or activity-feed features from
diff_against()output
Under The Hood
Architecture
Attaching HistoricalRecords() to a model hooks into Django’s model metaclass at app-loading time to dynamically create a companion Historical<Model> model with its own table and migration, then wires post_save/post_delete/m2m_changed signals (signals.py) to write a snapshot row on every mutation. HistoryManager/HistoricalQuerySet (manager.py) attach the .history accessor used to query, diff, and revert past state, while admin.py (375 lines) overrides the standard ModelAdmin change-history views to render diffs and drive reverts, and middleware.py exposes a thread/context-local so the acting user can be attached without threading it through every call site. Because the historical model and its signal wiring sit at the center of every tracked model’s save/delete path, changing that core descriptor is the one place that would ripple through every consumer and its migrations.
Tech Stack
The library is a pure Django app with a single runtime dependency, django>=5.2, targeting Python 3.10 through 3.14; CI runs the full cross-product of supported Python and Django versions (including Django’s main branch) against Postgres, MySQL, and SQLite services. Packaging uses hatchling with hatch-vcs for git-tag-derived versioning, and documentation is built with Sphinx and published via Read the Docs. There is no frontend build step — admin diff views are rendered with plain Django templates and templatetags.
Code Quality
The test suite spans admin, manager, middleware, model, signal, template, and command behavior across dedicated test modules, plus a separate registry-tests package and a custom-user fixture app, all exercised through runtests.py/tox across the full Python×Django×database matrix in CI. Pre-commit enforces bandit for security scanning, black for formatting, flake8 and isort for linting/import order, and codespell, with coverage tracked (branch coverage enabled) via Codecov. Errors are raised through a dedicated exceptions module rather than silently swallowed, and newer code uses TYPE_CHECKING imports and dataclasses for stronger typing.
What Makes It Unique
Rather than logging changes into a generic JSON or key-value audit table, django-simple-history generates a real parallel Django model — with the same field types, indexes, and query interface as the tracked model — for every history-enabled model, so history rows stay natively queryable and admin-browsable instead of requiring a separate deserialization layer. Combined with register() for tracking models you don’t control and middleware-based user attribution, it lets teams retrofit a full audit trail onto an existing Django project with minimal code changes.
Used by 2 apps in this directory
Flagsmith
Developer Tools · Devops · Ab Testing Experimentation
Open-source feature flagging, remote config, and A/B/multivariate testing platform for web, mobile, and server-side apps — self-host or use the hosted SaaS.
Horilla
Human Resources · ERP
Open-source HRMS covering recruitment, attendance, payroll, and biometrics in one self-hosted Django application.