user2355278
user2355278

Reputation: 395

Django - logging changes to a model

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.

Upvotes: 8

Views: 12771

Answers (3)

moein zed
moein zed

Reputation: 11

you can use GenericForeignKey to create logger model like:

class Logging(models.Model):
    value = models.CharField(max_length=100)
    created_at = models.DateTimeField(auto_now_add=True)
    object_id = models.PositiveIntegerField(null=True, blank=True)
    content_object = GenericForeignKey('content_type', 'object_id')

    class Meta:
        indexes = [models.Index(fields=['content_type', 'object_id']), ]
        ordering = ['-created_at']

and create an instance of it if you really wanna be specific you can also add a field_name value to that model

Upvotes: 0

jurms22
jurms22

Reputation: 121

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)

Upvotes: 6

Olivier Pons
Olivier Pons

Reputation: 15778

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'
        },
    },
}

Upvotes: 1

Related Questions