[Answer]-Tracking of field changes in Django

1👍

django-simple-history should solve your problem.

POSTING_CHOICES = (                     
    ('O', 'Open'),                    
    ('P', 'Pause'),                   
    ('C', 'Close'),
)                  

class Job_posting(models.Model):
    posting_status = models.CharField(max_length=1, choices=POSTING_CHOICES)
    history = HistoricalRecords()

Testing:

# jp1 = Job_posting.objects.create(posting_status='O')
# jp1.posting_status = 'P'
# jp1.save()
# jp1.history.last().posting_status
u'O'

Note that history is a per-model and not a per-field state.

👤dhke

0👍

Maybe an unorthodox solution, but maybe make a clone of your model on the pre_save signal, then on the post_save signal check if there are any clones, check for differences, and then create history items for any changes. E.g.

class BlogPost(models.Model):
    posting_status = models.CharField(max_length=30)
    # Using a CharField because your question doesn't make a lot of sense for a BooleanField, if it's changed and it's True then it was surely False before.

class Clone(BlogPost):
    blogpost_id = models.PositiveIntegerField()

# You'll also need a place to store the histories:

class HistoryItem(models.Model):
    blogpost = models.ForeignKey(BlogPost, related_name='posting_status_history')
    timestamp = models.DateTimeField(auto_now_add=True)
    old_value = models.CharField(max_length=30)
    new_value = models.CharField(max_length=30)

# Then simple dirty checking of the field before and after it's saved

@receiver(pre_save, sender=BlogPost)
def create_clone(sender, **kwargs):
    instance = kwargs.pop('instance', None)
    created = kwargs.pop('created', False)
    if not created and instance is not None:
        old_instance = BlogPost.objects.get(id=instance.id)  # This is pre-save so the object in db will have old values
        Clone.objects.create(blogpost_id=instance.id, posting_status=old_instance.posting_status, commit=True)

@receiver(post_save, sender=BlogPost)
def check_changes(sender, **kwargs):
    instance = kwargs.pop('instance', None)
    created = kwargs.pop('created', False)
    if not created and instance is not None:
        old_instance = Clone.objects.filter(blogpost_id=instance.id)
        old_value = old_instance.posting_status
        new_value = instance.posting_status
        if old_value != new_value:
            HistoryItem.objects.create(blogpost=instance, old_value=old_value, new_value=new_value)
        old_instance.delete()  # Clear up the clone

After that your BlogPost items with changes will have a posting_status_history related object manager, which you can query with blogpost.posting_status_history.all()

Leave a comment