Django Reversion
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.
Warnings
- breaking 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.
- breaking 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.
- gotcha 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.
- gotcha 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.
- gotcha 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.
Install
-
pip install django-reversion
Imports
- register
from reversion import register
- VersionAdmin
from reversion.admin import VersionAdmin
- create_revision
from reversion import create_revision
- get_for_object
from reversion import get_for_object
Quickstart
import os
# settings.py (simplified for demonstration)
INSTALLED_APPS = [
# ... other apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'reversion',
'myapp',
]
# myapp/models.py
from django.db import models
from reversion import register
@register()
class MyModel(models.Model):
name = models.CharField(max_length=100)
value = models.IntegerField(default=0)
def __str__(self):
return self.name
# myapp/admin.py
from django.contrib import admin
from reversion.admin import VersionAdmin
from .models import MyModel
@admin.register(MyModel)
class MyModelAdmin(VersionAdmin):
pass
# Example usage in a view or management command (assuming MyModel is created)
from django.db import transaction
from reversion import create_revision, set_comment
def update_my_model(obj_id, new_name, comment):
with create_revision():
set_comment(comment)
with transaction.atomic():
obj = MyModel.objects.get(id=obj_id)
obj.name = new_name
obj.save()
# Other related model saves can go here within the same revision block
# reversion.add_to_revision(related_obj)
# To retrieve history:
from reversion import get_for_object
my_instance = MyModel.objects.first() # Get an existing instance
if my_instance:
versions = get_for_object(my_instance).order_by('-revision__date_created')
for version in versions:
print(f"Version {version.pk}: {version.field_dict['name']} (Comment: {version.revision.comment}, User: {version.revision.user})")