Model or dataset
django-commons/django-simple-history avatar
django-commons/django-simple-history

django-simple-history: versioned model rows for Django, with admin revert

Store model history and view/revert changes from admin site.

2,463 stars508 forksPythonBSD-3-Clause

At a glance

What is it?
django-simple-history writes a new historical row on every create, update and delete of a registered model, then exposes that history in the admin. It is a good fit for Django teams that want audit trails without writing their own shadow tables.
Who is it for?
Adopt django-simple-history when you need row-level history of Django models and want revert available in the admin without building shadow tables yourself. Do not adopt it if you need history for bulk operations without extra wiring, or if you want field-level diffs rather than full row snapshots.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 13 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem django-simple-history solves

Django models are mutable. When a row changes, the previous values are gone unless you wrote them somewhere. Reconstructing who changed a field, what it looked like before, and when it happened usually means adding a second table, a signal handler, and an admin page. django-simple-history packages that pattern: the README states it "stores Django model state on every create/update/delete." The audience is Django developers who need an audit trail or a revert path and would rather not maintain the plumbing themselves. It is not a general event log or a diff engine. Each historical record is a copy of the model row at that point in time, plus metadata the library adds.

How the historical table is produced

The mechanism is a parallel model per registered model. You register a model with the library, and it creates a historical model whose fields mirror the original plus bookkeeping fields. Writes to the original model produce new rows in the historical table. Because the historical model is a real Django model, it is queryable through the ORM, and the admin integration renders it. The admin integration is what gives you view and revert without writing views: the README describes the package as storing history and viewing or reverting changes from the admin site. Two consequences follow from this design. First, storage grows with write volume, not with the number of distinct rows, so a frequently updated model accumulates many historical rows. Second, history is per model, so a change that touches several related models produces separate historical entries rather than one transaction-shaped record. The README does not document a cross-model grouping feature.

Installing django-simple-history and recording your first change

The package is published on PyPI as django-simple-history, and the project metadata declares django>=5.2 as its only runtime dependency with requires-python >=3.10. The README points readers to the documentation at the Read the Docs site for setup instructions, and the repository lists a docs/ directory alongside the simple_history package. The README itself does not print an install command, so the only package name it gives is the distribution name django-simple-history, and the only app name it gives is simple_history. After registering a model, the historical model needs its own table, which in Django means generating and applying a migration; the repository's own test tooling drives the suite through tox, and the Makefile exposes targets such as test, docs and format. What you should see once the historical table exists is a second table for the model's history. Create or update an instance through the ORM, then open the object in the admin. The history link on the change form lists the recorded states, and the revert action is available from that view. If no history rows appear, the usual cause is that the model was never registered, so no historical model was generated in the first place.

Where the snapshot model breaks down

Bulk writes are the clearest limitation. Operations that bypass per-instance saves do not produce the same history that a normal save does, and the README does not document query-level bulk behaviour. If your application updates thousands of rows with queryset updates, you should not assume django-simple-history captures them. The same caution applies to deletes issued at the queryset level. There is also a cost question: every tracked write becomes two writes, one for the row and one for the historical row, so models with hot update paths pay for history on every request that saves them. Finally, the historical record is a full row copy, not a field-level diff, which makes it easy to restore a state but less convenient if you only want to know which field changed between two versions. That question is answerable by comparing two historical rows, but the library does not present it as a diff.

django-simple-history compared with django-auditlog and django-reversion

The search data around this project pairs it with django-auditlog and django-reversion, and the difference is the unit of record. django-simple-history creates one historical model per tracked model, so history is a first-class model you can query, filter and join like any other table. django-auditlog takes an event-oriented approach: it records log entries describing actions, which suits questions like "what happened to this object and who did it" more than "what did this row look like at time T." django-reversion also targets versioning, and it is built around the idea of revisions, which group changes made together; that grouping is useful when one logical edit touches several models at once, which is exactly the case where per-model history tables leave you assembling the picture yourself. None of these is strictly better. If you want a queryable mirror table per model and admin revert, django-simple-history is the direct fit. If you want revisions that span models, or an action log, look at the other two.

Maintenance, releases and licence

The repository is not archived, and the last push was on 2026-09-17. Recent releases listed are 3.13.0 on 2026-07-22, 3.12.0 on 2026-06-22, and 3.11.0 on 2025-12-11. The README publishes a support table mapping Django versions to Python versions: Django 5.2 with Python 3.10 through 3.14, and Django 6.0 and 6.1 with Python 3.12 through 3.14, with main on 3.12 through 3.14. The project metadata sets requires-python to >=3.10 and depends on django>=5.2, so older Django lines are outside the declared support range. Upgrade cost is mostly migration cost: each tracked model has its own historical table, and schema changes to the original model require corresponding migrations for the historical model. The licence is BSD 3-Clause, per the README and the LICENSE.txt file in the repository. That is a permissive licence, but read the actual text and your own organisation's policy rather than treating any summary as legal advice.

Editorial conclusion

Adopt django-simple-history when you need row-level history of Django models and want revert available in the admin without building shadow tables yourself. Do not adopt it if you need history for bulk operations without extra wiring, or if you want field-level diffs rather than full row snapshots. Before committing, verify the Django and Python combination you run against the support table in the README, and check that your write paths do not use bulk_update or queryset-level deletes that the docs do not cover.

Frequently asked questions

How do I install django-simple-history?

The README does not print an install command; it points to the documentation at the Read the Docs site, and the project metadata gives the distribution name django-simple-history with django>=5.2 as its only runtime dependency. The app name to register is simple_history.

What is django-simple-history used for?

It stores Django model state on every create, update and delete, and lets you view or revert those changes from the admin site. It is aimed at Django developers who need a history of row changes without maintaining their own shadow tables.

How does django-simple-history differ from django-reversion?

django-simple-history keeps a historical model per tracked model, so history is queryable per table. django-reversion is built around revisions that group changes together, which suits edits that span several models at once.

Is django-simple-history an alternative to django-auditlog?

They record different units. django-simple-history stores row snapshots in a per-model historical table, while django-auditlog takes an event-oriented approach and records log entries describing actions. Which one fits depends on whether you need queryable row states or an action log.

Official sources

  1. django-commons/django-simple-history on GitHub
  2. License: BSD-3-Clause
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/django-commons-django-simple-history.svg)](https://hysenlabs.com/projects/django-commons-django-simple-history)