8

I'm in the middle of porting an application from Codeigniter to Django. One of the features I want to try and re-create in Django is the ability to log any changes to model field values.

Where is the best place to put it? I have tried to put it in model & form save methods but not having any luck at the moment. Has anyone go any examples?

Basically:

if orig.emp_name != self.emp_name: ##Create record in changes table with old value, new value and date/time of the change

Is it possible to loop through all ModelForm fields to check for changes in values? I can type the above for each field but it would be better if it could be in a loop.

user2355278
  • 375
  • 2
  • 5
  • 15
  • Checkout this thread http://stackoverflow.com/questions/1355150/django-when-saving-how-can-you-check-if-a-field-has-changed/13842223#13842223 to get good point to start – iperelivskiy Oct 12 '13 at 16:59
  • This is a fairly complex topic. Apart from the discussion livskiy pointed to make sure to check out the [Model auditing and History-Grid](https://www.djangopackages.com/grids/g/model-audit/) on Django Packages. – arie Oct 12 '13 at 17:04

2 Answers2

6

Here's how I've handled this and it's worked well so far:

# get current model instance to update
instance = MyModel.objects.get(id=id)

# use model_to_dict to convert object to dict (imported from django.forms.models import model_to_dict)
obj_dict = model_to_dict(instance)

# create instance of the model with this old data but do not save it
old_instance = MyModel(**obj_dict)

# update the model instance (there are multiple ways to do this)
MyModel.objects.filter(id=id).update(emp_name='your name') 

# get the updated object
updated_object = MyModel.objects.get(id=id)

# get list of fields in the model class
my_model_fields = [field.name for field in cls._meta.get_fields()]

# get list of fields if they are different
differences = list(filter(lambda field: getattr(updated_object, field, None)!= getattr(old_instance, field, None), my_model_fields))

The differences variable will give you the list of fields that are different between the two instances. I also found it helpful to add which model fields I don't want to check for differences (e.g. we know the updated_date will always be changed, so we don't need to keep track of it).

skip_diff_fields = ['updated_date']

my_model_fields = []
for field in cls._meta.get_fields():
    if field.name not in skip_diff_fields:
        my_model_fields.append(field.name)
jurms22
  • 101
  • 1
  • 3
1

Use the signal principles: pre_save, post_save, pre_delete, post_delete and so on.

Here

But if it's temporary, I prefer the way to configure the logging of ALL queries in settings.py: add this in the end of your settings.py and adapt it to your needs:

LOGGING = {
    'disable_existing_loggers': False,
    'version': 1,
    'handlers': {
        'console': {
            # logging handler that outputs log messages to terminal
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',  # message level to be written to console
        },
    },
    'loggers': {
        '': {
            # this sets root level logger to log debug and higher level
            # logs to console. All other loggers inherit settings from
            # root level logger.
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False, # this tells logger to send logging message
                                # to its parent (will send if set to True)
        },
        'django.db': {
            # django also has database level logging
            'level': 'DEBUG'
        },
    },
}
Olivier Pons
  • 15,363
  • 26
  • 117
  • 213