10

I have a model with a text field, that needs to be versioned.

class Book(models.Model):
    title = models.CharField(max_length=100)
    summary = models.TextField()

The expected behavior is as follows:

  • If i create a new book with a summary, the text will be saved normally
  • If the summary is updated, the old state needs to be stored somewhere with a version number and timestamp
  • It should be easily possible to query for the current, a range of or specific versions
  • Only the field summary should be versioned, not the whole model

How should i do this?

Alp
  • 29,274
  • 27
  • 120
  • 198

1 Answers1

13

Got it myself.

First create a new model called SummaryHistory:

class SummaryHistory(models.Model):
    version = models.IntegerField(editable=False)
    book = models.ForeignKey('Book')
    text = models.TextField()

    class Meta:
        unique_together = ('version', 'book',)

    def save(self, *args, **kwargs):
        # start with version 1 and increment it for each book
        current_version = SummaryHistory.objects.filter(book=self.book).order_by('-version')[:1]
        self.version = current_version[0].version + 1 if current_version else 1
        super(SummaryHistory, self).save(*args, **kwargs)

Now extend the initial model as follows:

class Book(models.Model):
    title = models.CharField(max_length=100)
    summary = models.TextField()

    def summary_history(self):
        return SummaryHistory.objects.filter(book=self).order_by('-version')

    def save(self, *args, **kwargs):
        super(Book, self).save(*args, **kwargs)
        # save summary history
        summary_history = self.summary_history()
        if not summary_history or self.summary != summary_history[0].text:
            newSummary = SummaryHistory(book=self, text=self.summary)
            newSummary.save()

Now, every time you update a book, a new incremented version of the summary for the specific book will be created unless it has not changed.

Alp
  • 29,274
  • 27
  • 120
  • 198
  • Looks good but is the references to `community` seem like a typos. If this works for you, please approve your own answer to your question! :) – jathanism May 10 '12 at 19:06
  • thank you, i fixed the wrong parts. approval works after 2 days. – Alp May 10 '12 at 19:09
  • 1
    I recommend using Signals instead of overriding the Save method. https://docs.djangoproject.com/en/dev/topics/signals/ – dannyroa May 10 '12 at 21:24
  • could you elaborate on how to do that in a seperate answer? did not use signals yet – Alp May 10 '12 at 21:48
  • 1
    Here's a discussion on when to use Signals or when to override save: http://stackoverflow.com/questions/5597378/django-when-to-customize-save-vs-using-post-save-signal – dannyroa May 11 '12 at 00:39