I'm working on a Django project for which I'll need to store the historic information of many models.
So far, I'm using the following strategy:
- Memoize the relevant values in
- When saving, check if a relevant value has changed.
- If a relevant value has changed, save memoized values into a "history" model.
My code goes as follows:
from django.db import models from core.models import User class Spam(models.Model): user= models.OneToOneField(User, on_delete=models.PROTECT, related_name='spam') ham = models.CharField(max_length=10) eggs = models.IntegerField() def __init__(self, *args, **kwargs): super(Spam, self).__init__(*args, **kwargs) self.__relevant_fields = ['ham', 'eggs'] self._memoize() def _memoize(self): for f in self.__relevant_fields: setattr(self, '__prev_%s' % f, getattr(self, f)) def _get_prev_value(self, f): return getattr(self, '__prev_%s' % f) def _dirty_field(self, f) -> bool: return getattr(self, '__prev_%s' % f) != getattr(self, f) def _dirty(self) -> bool: return any([self._dirty_field(f) for f in self.__relevant_fields]) def _save_history(self): history = dict() for f in self.__relevant_fields: history[f] = self._get_prev_value(f) self.user.spam_history.create(**history) def save(self, *args, **kwargs): super(Spam, self).save(*args, **kwargs) if self._dirty(): self._save_history() class SpamHistory(models.Model): user = models.ForeignKey(User, on_delete=models.PROTECT, related_name='spam_history') ham = models.CharField(max_length=10) eggs = models.IntegerField()
Although this approach works, there are some things that make me uncomfortable:
- I need to do this for many models, so I'm repeating myself a lot! I'm looking into inheritance, mixins or the like, but so far I haven't found a right way to make it work.
- Is this the best approach to achieve what I want? Considering that I will need to fetch historic records frequently, I need the "history" model for each model I need to keep history from.
So, my specific questions are:
- Am I implementing a good strategy for keeping the history of my model(s)?
- Is there a way to improve this strategy by using an inheritance or mixin strategy?