Storing the edit history of a django model in anot

2020-07-18 03:45发布

问题:

I have two models lets say:

class superfields(Model):
    fieldA = models.FloatField()
    fieldB = models.FloatField()
    class Meta:
        abstract = True

class my_model( superfields ):
    def has_history( self ):
        return self.my_model_history_set.count() > 0

class my_model_history( superfields ):
    reason = models.TextField()
    mymodel = models.ForeignKey( my_model )

'my_model' is populated with data (under fieldA and fieldB). Whenever someone edits 'my_model's fields and saves, I don't want to save the change in this model but want to store it as a new row with all values in 'my_model_history', in addition to a 'reason' field while 'my_model' data stays the same.

What is the best way to approach this scenario in terms of custom templates, custom views, model admins etc etc. Am I doing it correctly?

To give my question above some sense, in my project, the nature of data under 'my_model' is market prices and I need to maintain a history of all the market prices ever edited with a 'reason' for the edit.

回答1:

Instead of editing an existing entry, why not use that entry as initial data for a form to create a new instance? The new object gets saved, the original stays the same...



回答2:

My Solution:

yes. A simple and quick solution I am following is as follows: I create three models similar to this:

class my_super_abstract_model(Model):
    #All fields I need to keep a history for:
    fieldA = models.FloatField()
    fieldB = models.FloatField()
    class Meta:
        abstract = True

class my_model( my_super_abstract_model ):
    def has_history( self ):
        return self.my_model_history_set.count() > 0

class my_model_history( my_super_abstract_model ):
    reason = models.TextField()
    history_entry_for = models.ForeignKey( my_model )

I've setup a signal:

pre_save.connect( create_history, 
                  sender = my_model_history )

and 'create history' to be called by the pre_save() signal before saving in my_model_history:

def create_history(sender, **kwargs):
    #get variables passed by the pre-save signal:
    history_model = kwargs['instance']
    # Get main model object
    main_model = history_model.history_entry_for
    # swap all common fields between history edit and main model (except id)  
    main_model_fields = [f.name for f in main_model._meta.fields]
    history_model_fields = [f.name for f in history_model._meta.fields]
    field_index = list( [f for f in history_model_fields if f in main_model_fields and f != 'id' and f != 'created_date' ] )
    #loop thru to swap values:
    for field_name in field_index:
        temp = getattr(main_model, field_name)
        setattr( main_model, field_name, getattr( history_model, field_name ) )
        setattr( history_model, field_name, temp)
    # After the swap, save main model object here 
    main_model.save()

Whenever user clicks on a my_model row for editing, I use 'my_model_history' to generate my edit form and populate it with the values from the user selected row. (Have written a view and template to do that)

So the edit form will now have:

  1. field A -populated with values from my_model data row
  2. field B -populated with values from my_model data row
  3. Reason -empty text box
  4. history_entry_for -hidden from view

User can now edit fieldA/fieldB. Enter a reason. Press save to trigger the signal above. Before saving,

  1. Signal will swap the values between the main model(old values) and history model(New values)
  2. Replace and save the main model row (with the new values).
  3. Insert and save a new row in the history model (with the old values) with a reason.

Hope it helps. Let me know if there are any further questions.



回答3:

I found an explanation on keeping detailed edit histories in the book 'pro Django' page 264. After a read through I'll try an implementation of what I need. Will post my approach here when I'm done