Changes between Version 5 and Version 6 of AuditTrail

08/27/2007 09:41:33 AM (11 years ago)
George Vilches <gav@…>



  • AuditTrail

    v5 v6  
    6060ForeignKeys and OneToOneFields are now supported both for saving and accessing the audit data.  However, it does not archive the contents of the ForeignKey table for the appropriate entries at the same time, and will fail if the ForeignKey a given audit entry is related to is deleted (including if you're auditing the ForeignKey table as well, it does not have a way to link the two audit tables together).
     62== Tracking Extra Information ==
     63Sometimes you need to track more information than is available in just the model.  For instance, you may want to know who is performing the change on a particular entry, or track some sort of state information about the system.  AuditTrail now supports this through the concept of "track fields".  These can be specified on a per-model basis or a global basis, and the per-model options will stack with the global ones (but per-model options cannot override global ones currently).  Here's an example:
     66def some_callback(instance):
     67    return `random.randrange(1, 99)` + 'trackable_val'
     69class Person(models.Model):
     70    first_name = models.CharField(maxlength=255)
     71    last_name = models.CharField(maxlength=255)
     72    salary = models.PositiveIntegerField()
     73    history = audit.AuditTrail(track_fields=(('extra_1', models.CharField(maxlength=50), 'hardcoded_value'), ('extra_2', models.CharField(maxlength=50), some_callback),))
     75    def __str__(self):
     76        return "%s %s" % (self.first_name, self.last_name)
     79The track_fields is a tuple of 3-tuples.  The 3-tuples are structured {{{ (field_name, type_of_field, value) }}}.  `type_of_field` can be any currently functioning field type, although see the [#Caveats Caveats] for issues related to ForeignKeys.  `value` can be either a static value or a callback function, which will get called at the time of the save/delete.  This means that if you want to do something involving threadlocals at runtime (for getting things out of the request, for instance) you can do it via this callback.
     81Assume we ran the example above with this new model.  You could then do the following:
     84>>> p_hist = person.history.all()
     85[<PersonAudit: John Public as of 2007-08-27 09:29:14>, <PersonAudit: John Public as of 2007-08-27 09:28:57>]
     86>>> p_hist[0].extra_1
     88>>> p_hist[0].extra_2
     92Currently, you cannot filter on these trackable columns, this should be fixable.
     94=== Global Track Fields ===
     96What if you have a field you want tracked on every model that supports history?  No problem!  In the root of your project, create a file called, and put something like this in it:
     99from django.db import models
     101def callback_func_ptr2(original_instance):
     102    import random
     103    return `random.randrange(1, 99)` + 'hardcoded_global_2'
     105# Populate the fields that every Audit model in this app will use.
     107    ('global_1', models.CharField(maxlength=50), 'hardcoded_global_1'),
     108    ('global_2', models.CharField(maxlength=20), callback_func_ptr2),
     113GLOBAL_TRACK_FIELDS is set up exactly the same way as the track_fields option passed into AuditTrail, and has the same uses and limitations.
    62115== Caveats ==
    63116This needs testing!  This has only been used in a few cases, there's plenty of possible room for strangeness.  It has specifically not been tested for things like safe (de-)serialization.
Back to Top