Give AlbumentationsX a star on GitHub — it powers this leaderboard

Star on GitHub

django-simple-history

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

Rank: #2301Downloads: 2,849,304 (30 days)Stars: 2,440Forks: 495

Description

|build-status| |docs| |coverage| |maintainability| |code-style| |downloads|

.. |pypi-version| image:: https://img.shields.io/pypi/v/django-simple-history.svg
   :target: https://pypi.org/project/django-simple-history/
   :alt: PyPI Version

.. |build-status| image:: https://github.com/django-commons/django-simple-history/actions/workflows/test.yml/badge.svg
   :target: https://github.com/django-commons/django-simple-history/actions/workflows/test.yml
   :alt: Build Status

.. |docs| image:: https://readthedocs.org/projects/django-simple-history/badge/?version=latest
   :target: https://django-simple-history.readthedocs.io/en/latest/?badge=latest
   :alt: Documentation Status

.. |coverage| image:: https://img.shields.io/codecov/c/github/django-commons/django-simple-history/master.svg
   :target: https://app.codecov.io/github/django-commons/django-simple-history?branch=master
   :alt: Test Coverage

.. |maintainability| image:: https://api.codeclimate.com/v1/badges/66cfd94e2db991f2d28a/maintainability
   :target: https://codeclimate.com/github/django-commons/django-simple-history/maintainability
   :alt: Maintainability

.. |code-style| image:: https://img.shields.io/badge/code%20style-black-000000.svg
   :target: https://github.com/psf/black
   :alt: Code Style

.. |downloads| image:: https://static.pepy.tech/badge/django-simple-history
   :target: https://pepy.tech/project/django-simple-history
   :alt: Downloads


``django-simple-history`` stores Django model state on every create/update/delete.

This app supports the following combinations of Django and Python:

==========  ========================
  Django      Python
==========  ========================
4.2         3.10, 3.11, 3.12, 3.13
5.0         3.10, 3.11, 3.12, 3.13
5.1         3.10, 3.11, 3.12, 3.13
5.2         3.10, 3.11, 3.12, 3.13, 3.14
6.0         3.12, 3.13, 3.14
main        3.12, 3.13, 3.14
==========  ========================

Getting Help
------------

Documentation is available at https://django-simple-history.readthedocs.io/en/stable/

Pull requests are welcome. Read the `CONTRIBUTING`_ file for tips on
submitting a pull request.

.. _CONTRIBUTING: https://github.com/django-commons/django-simple-history/blob/master/CONTRIBUTING.rst

License
-------

This project is licensed under the
`BSD 3-Clause license <https://choosealicense.com/licenses/bsd-3-clause/>`_.

====

Changelog
=========

3.11.0 (2025-12-09)
-------------------

- Added support for Python 3.14 (gh-1529)
- Added support for Django 6.0 (gh-1529)
- Dropped support for Python 3.9, which reached end-of-life on 2025-10-31 (gh-1560)
- Added Ukrainian localization (gh-1547)

3.10.1 (2025-06-20)
-------------------

- Fixed changelog syntax to support PyPI packaging (gh-1499)

3.10.0 (2025-06-20)
-------------------

- Tests are no longer bundled in released wheels (gh-1478)
- Move repository to the Django Commons organization (gh-1391)

3.9.0 (2025-01-26)
------------------

- Removed the ``simple_history_admin_list.display_list()`` template tag that was
  deprecated in version 3.6.0 (gh-1444)

3.8.0 (2025-01-23)
------------------

- Made ``skip_history_when_saving`` work when creating an object - not just when
  updating an object (gh-1262)
- Improved performance of the ``latest_of_each()`` history manager method (gh-1360)
- Fixed issue with deferred fields causing DoesNotExist error (gh-678)
- Added HistoricOneToOneField (gh-1394)
- Updated all djangoproject.com links to reference the stable version (gh-1420)
- Dropped support for Python 3.8, which reached end-of-life on 2024-10-07 (gh-1421)
- Added support for Django 5.1 (gh-1388)
- Added pagination to ``SimpleHistoryAdmin`` (gh-1277)
- Fixed issue with history button not working when viewing historical entries in the
  admin (gh-527)
- Added support for Django 5.2 (gh-1441)
- ``simple_history_admin_list.display_list()`` *was planned to be removed in this
  release, but it was overlooked, and will instead be removed in 3.9.0*

3.7.0 (2024-05-29)
------------------

- Dropped support for Django 3.2, which reached end-of-life on 2024-04-01 (gh-1344)
- Removed the temporary requirement on ``asgiref>=3.6`` added in 3.5.0,
  now that the minimum required Django version is 4.2 (gh-1344)
- Migrated package building from using the deprecated ``setup.py`` to using
  ``pyproject.toml`` (with Hatchling as build backend);
  ``setup.py`` has consequently been removed (gh-1348)
- Added ``django>=4.2`` as an installation dependency, to mirror the minimum version
  tested in our CI (gh-1349)

3.6.0 (2024-05-26)
------------------

- Support custom History ``Manager`` and ``QuerySet`` classes (gh-1280)
- Renamed the (previously internal) admin template
  ``simple_history/_object_history_list.html`` to
  ``simple_history/object_history_list.html``, and added the field
  ``SimpleHistoryAdmin.object_history_list_template`` for overriding it (gh-1128)
- Deprecated the undocumented template tag ``simple_history_admin_list.display_list()``;
  it will be removed in version 3.8 (gh-1128)
- Added ``SimpleHistoryAdmin.get_history_queryset()`` for overriding which ``QuerySet``
  is used to list the historical records (gh-1128)
- Added ``SimpleHistoryAdmin.get_history_list_display()`` which returns
  ``history_list_display`` by default, and made the latter into an actual field (gh-1128)
- ``ModelDelta`` and ``ModelChange`` (in ``simple_history.models``) are now immutable
  dataclasses; their signatures remain unchanged (gh-1128)
- ``ModelDelta``'s ``changes`` and ``changed_fields`` are now sorted alphabetically by
  field name. Also, if ``ModelChange`` is for an M2M field, its ``old`` and ``new``
  lists are sorted by the related object. This should help prevent flaky tests. (gh-1128)
- ``diff_against()`` has a new keyword argument, ``foreign_keys_are_objs``;
  see usage in the docs under "History Diffing" (gh-1128)
- Added a "Changes" column to ``SimpleHistoryAdmin``'s object history table, listing
  the changes between each historical record of the object; see the docs under
  "Customizing the History Admin Templates" for overriding its template context (gh-1128)
- Fixed the setting ``SIMPLE_HISTORY_ENABLED = False`` not preventing M2M historical
  records from being created (gh-1328)
- For history-tracked M2M fields, adding M2M objects (using ``add()`` or ``set()``)
  used to cause a number of database queries that scaled linearly with the number of
  objects; this has been fixed to now be a constant number of queries (gh-1333)

3.5.0 (2024-02-19)
------------------

- Fixed ``FieldError`` when creating historical records for many-to-many fields with
  ``to="self"`` (gh-1218)
- Allow ``HistoricalRecords.m2m_fields`` as str (gh-1243)
- Fixed ``HistoryRequestMiddleware`` deleting non-existent
  ``HistoricalRecords.context.request`` in very specific circumstances (gh-1256)
- Added ``custom_historical_attrs`` to ``bulk_create_with_history()`` and
  ``bulk_update_with_history()`` for setting additional fields on custom history models
  (gh-1248)
- Passing an empty list as the ``fields`` argument to ``bulk_update_with_history()`` is
  now allowed; history records will still be created (gh-1248)
- Added temporary requirement on ``asgiref>=3.6`` while the minimum required Django
  version is lower than 4.2 (gh-1261)
- Small performance optimization of the ``clean-duplicate_history`` command (gh-1015)
- Support Simplified Chinese translation (gh-1281)
- Added support for Django 5.0 (gh-1283)
- Added support for Python 3.13 (gh-1289)

3.4.0 (2023-08-18)
------------------

- Fixed typos in the docs
- Added feature to evaluate ``history`` model permissions explicitly when
  ``SIMPLE_HISTORY_ENFORCE_HISTORY_MODEL_PERMISSIONS`` is set to ``True``
  in ``settings`` (gh-1017).
- Fixed ``SimpleHistoryAdmin`` not properly integrating with custom user models (gh-1177)
- Support Indonesian translation (gh-1198)
- Support Urdu translation (gh-1199)
- Support Norwegian Bokmål translation (gh-1210)
- Dropped support for Python 3.7, which reached end-of-life on 2023-06-27 (gh-1202)
- Dropped support for Django 4.0, which reached end-of-life on 2023-04-01 (gh-1202)
- Added support for Django 4.2 (gh-1202)
- Made ``bulk_update_with_history()`` return the number of model rows updated (gh-1206)
- Fixed ``HistoryRequestMiddleware`` not cleaning up after itself (i.e. deleting
  ``HistoricalRecords.context.request``) under some circumstances (gh-1188)
- Made ``HistoryRequestMiddleware`` async-capable (gh-1209)
- Fixed error when setting ``table_name`` with ``inherit=True`` (gh-1195)

3.3.0 (2023-03-08)
------------------

- Made it possible to use the new ``m2m_fields`` with model inheritance (gh-1042)
- Added two signals: ``pre_create_historical_m2m_records`` and ``post_create_historical_m2m_records`` (gh-1042)
- Added ``tracked_fields`` attribute to historical models (gh-1038)
- Fixed ``KeyError`` when running ``clean_duplicate_history`` on models with ``excluded_fields`` (gh-1038)
- Added support for Python 3.11 (gh-1053)
- Added Arabic translations (gh-1056)
- Fixed a code example under "Tracking many to many relationships" (gh-1069)
- Added a ``--base-manager`` option to the ``clean_duplicate_history`` management command (gh-1115)

3.2.0 (2022-09-28)
------------------

- Fixed typos in the docs
- Removed n+1 query from ``bulk_create_with_history`` utility (gh-975)
- Started using ``exists`` query instead of ``count`` in ``populate_history`` command (gh-982)
- Add basic support for many-to-many fields (gh-399)
- Added support for Django 4.1 (gh-1021)

3.1.1 (2022-04-23)
------------------

Full list of changes:

- Fix py36 references in pyproject.toml (gh-960)
- Fix local setup.py install versioning issue (gh-960)
- Remove py2 universal wheel cfg - only py3 needed now (gh-960)


3.1.0 (2022-04-09)
------------------

Breaking Changes:

- Dropped support for Django 2.2 (gh-968)
- Dropped support for Django 3.1 (gh-952)
- Dropped support for Python 3.6, which reached end-of-lif