Django track changes to a model

Django track changes to a model.

# models.py
from django.contrib.auth.models import User
from django.db import models
from django.db.models import ManyToOneRel

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    location = models.CharField(blank=True, max_length=30)
    favorite_color = models.CharField(blank=True, max_length=30)

    def __unicode__(self):
        return u'{0} {1}'.format(self.first_name, self.last_name)

    def __setattr__(self, name, value):
        super(Person, self).__setattr__(name, value)
        if name in self._meta.get_all_field_names():
            try:
                getattr(self, '_old_{0}'.format(name))
            except AttributeError:
                setattr(self, '_old_{0}'.format(name), value)

    def save(self, *args, **kwargs):
        super(Person, self).save(*args, **kwargs)

        for field_name in self._meta.get_all_field_names():
            field = self._meta.get_field(field_name)
            if not isinstance(field, ManyToOneRel):
                try:
                    old_value = getattr(self, '_old_{0}'.format(field_name))
                except AttributeError:
                    continue

                new_value = getattr(self, field_name)
                if new_value != old_value:
                    change = PersonChange()
                    change.obj = self
                    change.field_name = field_name
                    change.old_value = old_value
                    change.new_value = new_value
                    change.changed_by = self._user if hasattr(self, '_user') else None
                    change.save()

class PersonChange(models.Model):
    obj = models.ForeignKey(Person)
    field_name = models.CharField(db_index=True, max_length=255)
    old_value = models.TextField(blank=True, null=True)
    new_value = models.TextField(blank=True, null=True)
    changed_by = models.ForeignKey(User, blank=True, null=True)
    changed_at = models.DateTimeField(auto_now_add=True)
# admin.py
from django.contrib import admin

from .models import Person
from .models import PersonChange

class PersonAdmin(admin.ModelAdmin):
    list_display = ('pk', 'first_name', 'last_name', 'location', 'favorite_color',)
    search_fields = ('id', 'first_name', 'last_name', 'location', 'favorite_color',)

    def save_model(self, request, obj, form, change):
        obj._user = request.user
        super(PersonAdmin, self).save_model(request, obj, form, change)

class PersonChangeAdmin(admin.ModelAdmin):
    list_display = ('pk', 'obj', 'field_name', 'old_value', 'new_value', 'changed_at', 'changed_by')

admin.site.register(Person, PersonAdmin)
admin.site.register(PersonChange, PersonChangeAdmin)

Comments

Leave a Reply