Full Model History

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# -*- coding: UTF-8 -*-

from django.db import models
from datetime import datetime

class FullHistory(models.Model):
    """ 
    Issues: Unique fields don't work (multiple versions of the same row may need to have the same value)
    """
    date_created     = models.DateTimeField(default=datetime.now, editable=False)
    date_updated     = models.DateTimeField(default=datetime.now, editable=False)

    class Meta:
        abstract = True
        ordering = ('date_updated',)

    def __init__(self, *args, **kwargs):
        # History classes must end in 'History', others must not.
        if self.__class__.__name__.endswith('History'):
            self._history_class = True
        else:
            self._history_class = False
        super(FullHistory, self).__init__(*args, **kwargs)


    def save(self, *args, **kwargs):
        if self._history_class:
            self._save_history_instance()
        else:
            self._save_non_history_instance()

        super(FullHistory, self).save(*args, **kwargs)

    def _save_history_instance(self):
        """ Save method for a History object.
        """
        # This updated instance must become a new object, 
        # no updating is possible for history classes
        self.id = None

    def _save_non_history_instance(self):
        """ Save method for a non-History object.
        """
        # Duplicate and reassign parent.
        for model, field in self._meta.parents.items():
            if getattr(self, '%s_id' % field.name) is not None:
                rel_obj = getattr(self, field.name)
                rel_obj.id = None
                rel_obj.save()
                setattr(self, '%s_id' % field.name, rel_obj.id)

        # Set the new update time on the non-archived version
        self.date_updated = datetime.now()

    def delete(self):
        # Only delete if this is not a "history" version
        if not self._history_class:
            self.save()
            super(FullHistory, self).delete()

More like this

  1. FieldsetForm by Ciantic 7 years ago
  2. Django admin inline ordering - javascript only implementation by ojhilt 1 year, 4 months ago
  3. Simple Admin-button linking to Databrowse by magicrebirth 4 years, 11 months ago
  4. Row-Level, URL-based permissions for FlatPages by bradmontgomery 4 years, 10 months ago
  5. Unobtrusive comment moderation by ubernostrum 7 years, 1 month ago

Comments

carljm (on December 8, 2008):

Brilliant. Way to get around all the hard stuff in the AuditTrail wiki page in one fell swoop.

#

willhardy (on December 10, 2008):

Thanks! I'll look at expanding on this and making it comprehensive. I've just realised what a cool snippet number I have. Excellent.

#

Killarny (on December 16, 2008):

I am also highly interested in this, as we've done something similar at work. Originally we intended to do the history at the DB level, but after designing the models to support that (a custom model that did nothing but provide read access to history entries) we decided to use Django for it.

Looking forward to seeing what you come up with as you expand it!

#

andybak (on June 26, 2009):

Take a look at django-reversion. That has admin integration and will work on third party apps just by changing a line in their admin.py

#

pjv (on July 23, 2009):

is this currently working for you against django 1.1 rc?

when i try to use it, i get the following error on save of an "Employee" instance:

/Users/paul/Documents/web_development/django_sites/test_project/history/models.pyc in save(self, *args, **kwargs)
     18 
     19             # Duplicate and reassign parent
---> 20             for model, field in self._meta.parents:
     21                 if getattr(self, '%s_id' % field.name) is not None:
     22                     rel_obj = getattr(self, field.name)

TypeError: 'ModelBase' object is not iterable

#

(Forgotten your password?)