Reputation: 395
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
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
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
Reputation: 15778
Use the signal principles: pre_save, post_save, pre_delete, post_delete and so on.
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