# history_tracking/mixins.py from django.db import models from django.conf import settings class HistoricalChangeMixin(models.Model): """Mixin for historical models to track changes""" id = models.BigIntegerField(db_index=True, auto_created=True, blank=True) history_date = models.DateTimeField() history_id = models.AutoField(primary_key=True) history_type = models.CharField(max_length=1) history_user = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL, related_name='+' ) history_change_reason = models.CharField(max_length=100, null=True) class Meta: abstract = True ordering = ['-history_date', '-history_id'] @property def prev_record(self): """Get the previous record for this instance""" try: return self.__class__.objects.filter( history_date__lt=self.history_date, id=self.id ).order_by('-history_date').first() except (AttributeError, TypeError): return None @property def diff_against_previous(self): prev_record = self.prev_record if not prev_record: return {} changes = {} for field in self.__dict__: if field not in [ "history_date", "history_id", "history_type", "history_user_id", "history_change_reason", "history_type", "id", "_state", "_history_user_cache" ] and not field.startswith("_"): try: old_value = getattr(prev_record, field) new_value = getattr(self, field) if old_value != new_value: changes[field] = {"old": str(old_value), "new": str(new_value)} except AttributeError: continue return changes @property def history_user_display(self): """Get a display name for the history user""" if hasattr(self, 'history_user') and self.history_user: return str(self.history_user) return None def get_instance(self): """Get the model instance this history record represents""" try: return self.__class__.objects.get(id=self.id) except self.__class__.DoesNotExist: return None