How to enable version control in Django

Auditing trail is a common requirement for many applications. By enabling this feature an user can see when, what and who did the changes. Pretty much like version controls for model data. A perfect audit trailing feature not only let’s to investigate the changes but also have the option to roll back to any point in a model instances history.

For many developers implementing auditing trail is a fun job to do and by doing so he or she might acquire some pragmatic skill in software engineering.

But when you are in hurry or with deadline you have to meet, then you might want to go for a usable plug-in. In the case of plug-in we all have that experience when a plug-in doesn’t just stays plug-in rather we have to do a lot of wiring to plug that in(I mean fixing dependency related stuffs). The plug-in I’m going to write today about is called django-reversion. Let’s start…..


  1. Install with pip: pip install django-reversion.
  2. Add 'reversion' to INSTALLED_APPS.
  3. Run migrate.

We are all set, now, let’s see how many line it takes to enable version control for our model. Suppose I have a very small model called Client and it has three attributes: name, occupation and date_of_birth.

Just open the file and add these lines to register your model with reversion:

from .models import *
from reversion.admin import VersionAdmin

class ClientModelAdmin(VersionAdmin):

After registering your model you have to run python createinitialrevisions each time so that reversion gets the chance to create necessary data structure for our model.

To see reversion in action go to you admin site , and create a new Client. After creating open detail of new entry and you’ll see a History button with gray background on your top right. Click that button, and you’ll see something like this:

Now make some changes and see the history, you’ll see the histories are nicely kept. Even you can view the exact version for the date of the entry and restore that.

You can enable versioning for as many models you want just not forget to run command to create initial revisions. Then the user who has the access to this admin site can see that model’s history and also can restore the instance from a previous version.

But wait, you may not want all the user’s to have permission for admin site. Even in your project, you may not even needed an admin site. So, just for this feature if user have to learn and navigate through a new type of interface, that doesn’t sounds neat.

Functionality of reversion can be integrated with your own interface in many ways. Here I’m going to show you by creating two api views.

First one will show the histories for a model object with relevant information. Create a view like this:

from rest_framework.decorators import api_view
from reversion.models import Version
from django.db.models import F
from .models import *
def client_history(request, client_id):
client = Client.objects.get(id=client_id)
versions = Version.objects.get_for_object(client)

data = versions.values('pk',

return Response({"data": data})

For history we are just showing some meta data for each versions. These meta data are stored in a foreign-keyed table revision with version object.

Now add this line in urlpatterns:

url(r'^api/client/(?P<client_id>[0-9]+)/history/$', views.client_history, name='client-history'),

When you access the url with a valid primary key for a client you’ll see a json response with date and time, action name and user. In my case, I’m seeing something like this:

Hmm, looks okay but could be better if long keys are replaced with something concise right? Change the data variable like this:

data = versions.values('pk',
Response with concise keys

Now, let’s create another view that will take both pk of the version and will show actual data for that version. Note that, version table is not dependent on model. Thus it’s going to be super simple like this:

def client_version(request, version_id):
v = Version.objects.get(id=version_id)
return Response({"data": v.field_dict})

And the url can be something like this:

url(r'^api/client/version/(?P<version_id>[0-9]+)/$', views.client_version, name='client-version'),

Then the response:

Content-Type: application/json
Vary: Accept
"data": {
"date_of_birth": "2017-11-03",
"occupation": "xTeacherN",
"id": 121,
"name": "John Doe"

Now we are almost done here. Lastly you can revert any object to a previous version by providing the versions id like this: Version.objects.get(id=15).revision.revert()

A little heads up for you is, this will not add a version log in the history like reverting from admin panel does. But if you need version log, its definitely doable. Just do some digging and you will figure it out.

Always finish antibiotic course!




Started(writing) with poetry, ended up with codes. Have a university degree on Biotechnology. Works and talks about Java, Python, JS. Follow me :)

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

12 Most Frequently Asked Questions Partners Ask About the Salesforce PADA

Installing Nvidia CUDA with cuDNN and Nvidia Container Toolkit on Ubuntu using Makefile

Rails Project 3 — Spotify/Vinyl

portworx install on-premisses kubernetes

Laravel Artisan command line usage examples 2020

The Cookbook: Build Matrix

AWS RDS Upgrades — Multi major version upgrade (9.5.20 to 12.2)

Axelar — new opportunities for dapp and blockchain ecosystems

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ruhshan Ahmed Abir

Ruhshan Ahmed Abir

Started(writing) with poetry, ended up with codes. Have a university degree on Biotechnology. Works and talks about Java, Python, JS. Follow me :)

More from Medium

CI/CD with Django and GitHub (with Video)

Deploy Django using Postgres, Nginx in digital ocean ubuntu droplet

Deploy Django using Postgres, Nginx in digital ocean

How to set up a Project in Django

Retrieving images using database and media in Django