{"id":3463,"library":"django-reversion","title":"Django Reversion","description":"Django Reversion is an actively maintained extension to the Django web framework that provides comprehensive version control for model instances. It allows developers to effortlessly track, revert, and manage changes made to their data, including the ability to recover deleted objects. The current version is 6.1.0, with a regular release cadence addressing compatibility and new features.","status":"active","version":"6.1.0","language":"en","source_language":"en","source_url":"https://github.com/etianen/django-reversion","tags":["django","version control","history","admin","audit"],"install":[{"cmd":"pip install django-reversion","lang":"bash","label":"Install with pip"}],"dependencies":[{"reason":"Core framework dependency, requires Django 4.2 or later.","package":"Django","optional":false},{"reason":"Requires Python 3.9 or later.","package":"Python","optional":false}],"imports":[{"note":"Decorator for enabling version control on models.","symbol":"register","correct":"from reversion import register"},{"note":"Provides version history and recovery in the Django admin interface.","symbol":"VersionAdmin","correct":"from reversion.admin import VersionAdmin"},{"note":"Context manager or decorator to group changes into a single revision.","symbol":"create_revision","correct":"from reversion import create_revision"},{"note":"The higher-level `reversion.get_for_object` is generally preferred over direct `Version.objects` queries.","wrong":"from reversion.models import Version; Version.objects.get_for_object(obj)","symbol":"get_for_object","correct":"from reversion import get_for_object"}],"quickstart":{"code":"import os\n\n# settings.py (simplified for demonstration)\nINSTALLED_APPS = [\n    # ... other apps\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'reversion',\n    'myapp',\n]\n\n# myapp/models.py\nfrom django.db import models\nfrom reversion import register\n\n@register()\nclass MyModel(models.Model):\n    name = models.CharField(max_length=100)\n    value = models.IntegerField(default=0)\n\n    def __str__(self):\n        return self.name\n\n# myapp/admin.py\nfrom django.contrib import admin\nfrom reversion.admin import VersionAdmin\nfrom .models import MyModel\n\n@admin.register(MyModel)\nclass MyModelAdmin(VersionAdmin):\n    pass\n\n# Example usage in a view or management command (assuming MyModel is created)\nfrom django.db import transaction\nfrom reversion import create_revision, set_comment\n\ndef update_my_model(obj_id, new_name, comment):\n    with create_revision():\n        set_comment(comment)\n        with transaction.atomic():\n            obj = MyModel.objects.get(id=obj_id)\n            obj.name = new_name\n            obj.save()\n        # Other related model saves can go here within the same revision block\n        # reversion.add_to_revision(related_obj)\n\n# To retrieve history:\nfrom reversion import get_for_object\nmy_instance = MyModel.objects.first() # Get an existing instance\nif my_instance:\n    versions = get_for_object(my_instance).order_by('-revision__date_created')\n    for version in versions:\n        print(f\"Version {version.pk}: {version.field_dict['name']} (Comment: {version.revision.comment}, User: {version.revision.user})\")\n","lang":"python","description":"To get started, install `django-reversion` and add it to your `INSTALLED_APPS`. Then, register your models using the `@reversion.register()` decorator or inherit `VersionAdmin` in your `admin.py` for automatic integration. The `create_revision()` context manager or decorator is used to group multiple changes into a single historical revision, and `get_for_object()` allows programmatic access to an object's version history."},"warnings":[{"fix":"Replace `reversion.signals.pre_revision_commit` or `post_revision_commit` signal connections with Django's `django.db.models.signals.pre_save` or `post_save` signals connected to the `reversion.models.Revision` model.","message":"In `v6.0.0`, the custom `pre_revision_commit` and `post_revision_commit` signals were removed. Users should now leverage Django's standard `pre_save` and `post_save` signals on the `Revision` model for similar functionality.","severity":"breaking","affected_versions":">=6.0.0"},{"fix":"Upgrade your Python environment to 3.9+ and your Django version to 4.2+ before upgrading to recent `django-reversion` versions.","message":"As of `v5.0.0`, support for Python 3.6 was dropped. The library now officially requires Python 3.9 or later, and Django 4.2 or later, as per `v6.1.0` requirements.","severity":"breaking","affected_versions":">=5.0.0 (Python), >=6.1.0 (Django)"},{"fix":"After significant model schema changes, it is often necessary to clear out old revision data for the affected models. Adding new fields is usually fine, but removing fields is problematic. Consider creating data migrations to handle or delete incompatible historical data.","message":"Changes to your model's schema (e.g., removing fields) can lead to `reversion.errors.RevertError: Could not load <Foo: bar> - incompatible version data` when attempting to restore older versions. This is because `django-reversion` stores versions as JSON, and schema migrations do not update this historical data.","severity":"gotcha","affected_versions":"All versions"},{"fix":"To version changes from bulk operations, wrap them explicitly within a `reversion.create_revision()` context manager and manually add affected objects using `reversion.add_to_revision()` if granular control is needed, or iterate and save objects individually.","message":"Bulk update operations (e.g., `QuerySet.update()`) do not trigger Django's `post_save` signals, which `django-reversion` relies on to create revisions. Consequently, changes made via bulk operations will not be recorded in version history automatically.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Convert all relative import statements in your Django apps to absolute imports to ensure `models.py` files are not unintentionally imported multiple times.","message":"The `reversion.RegistrationError: class 'myapp.MyModel' has already been registered with Reversion` commonly occurs due to models.py being imported twice, often caused by relative import statements in your codebase.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}