Reputation: 1138
I know there is reversion out there, I know there is fullhistory branch in django. BUT I would VERY much like to stick with original history of django objects. I just simply need to make sure I also save OLD and NEW values.
By default django just saves WHAT happened (e.g. update) but doesnt store the values. Anybody can point me to something (snippet or app) that slightly enhances the default history of django and stores also the values of edited attributes not just the fact that it was edited.
Now My history says
9-01-2012 12:55:02 chnaged: time
I want it to say
9-01-2012 12:55:02 chnaged: time from 6 to 8
UPDATE: ==> for anyone interested in my solution (based on answer below)..
Added HistoryModel(object) class to my models.py
class HistoryModel(object):
changed_fields = {}
Inherited in any of my models
Added a pre_save receiver to save old values:
@receiver(pre_save, sender=Customer)
def save_old_values(sender,**kwargs):
#dont delete using in eval
db_obj = sender.objects.get(pk=kwargs['instance'].pk)
for field in kwargs['instance']._meta.fields:
if not eval("db_obj." + str(field.get_attname_column()[0])) == eval("kwargs['instance']." + str(field.get_attname_column()[0])):
kwargs['instance'].changed_fields[field.get_attname_column()[0]] = "from "+str(eval("db_obj." + str(field.get_attname_column()[0]))) + " to " + str(eval("kwargs['instance']." + str(field.get_attname_column()[0])))
Overwrote the log_change method in my admin and collected object.changed_fields dict and stored it as a message
Upvotes: 2
Views: 1406
Reputation: 61
Updated version of @DavideBrunato's snippet that works in Django 4:
def construct_change_message(self, request, form, formsets, add=False):
change_message = []
if add:
change_message.append({"added": {}})
elif form.changed_data:
msg_list = []
for field in form.changed_data:
if form.initial[field] is not None and hasattr(form.fields[field], 'queryset'):
old_value = form.fields[field].queryset.get(id=form.initial[field])
else:
old_value = form.initial[field]
msg_list.append('\n"%s": "%s" > "%s"' % (field, old_value, form[field].value()))
change_message.append({"changed": {"fields": msg_list}})
if formsets:
with translation_override(None):
for formset in formsets:
for added_object in formset.new_objects:
change_message.append(
{
"added": {
"name": str(added_object._meta.verbose_name),
"object": str(added_object),
}
}
)
for changed_object, changed_fields in formset.changed_objects:
for form in formset.initial_forms:
if form.instance != changed_object:
continue
msg_list = []
for field in changed_fields:
if form.initial[field] is not None and hasattr(form.fields[field], 'queryset'):
old_value = form.fields[field].queryset.get(id=form.initial[field])
else:
old_value = form.initial[field]
msg_list.append('\n"%s": "%s" > "%s"' % (field, old_value, form[field].value()))
change_message.append(
{
"changed": {
"name": str(changed_object._meta.verbose_name),
"object": str(changed_object),
"fields": msg_list,
}
}
)
for deleted_object in formset.deleted_objects:
change_message.append(
{
"deleted": {
"name": str(deleted_object._meta.verbose_name),
"object": str(deleted_object),
}
}
)
return change_message
Upvotes: 0
Reputation: 752
To extend information about changes is sufficient an override of only ModelAdmin.construct_change_message
method. No need to add attributes (eg. changed_fields) on the models, because you can build the log messages from initial data of forms and formsets.
This is a version that records the previous value for every changed field:
def construct_change_message(self, request, form, formsets):
change_message = []
if form.changed_data:
msg_list = u''
for field in form.changed_data:
if form.initial[field] is not None and hasattr(form.fields[field], 'queryset'):
old_value = form.fields[field].queryset.get(id=form.initial[field]).__unicode__()
else:
old_value = form.initial[field]
msg_list = _("{0}field '{1}' from \"{2}\", ").format(msg_list, field, old_value)
change_message.append(capfirst(_(u'changed {0}.').format(msg_list[:-2])))
if formsets:
for formset in formsets:
for added_object in formset.new_objects:
change_message.append(_('Added %(name)s "%(object)s".')
% {'name': force_unicode(added_object._meta.verbose_name),
'object': force_unicode(added_object)})
for changed_object, changed_fields in formset.changed_objects :
for form in formset.initial_forms:
if form.instance != changed_object:
continue
msg_list = u''
for field in changed_fields:
if form.initial[field] is not None and hasattr(form.fields[field], 'queryset'):
old_value = form.fields[field].queryset.get(id=form.initial[field]).__unicode__()
else:
old_value = form.initial[field]
msg_list = _("{0}field '{1}' from \"{2}\", ").format(msg_list, field, old_value)
change_message.append(_('Changed %(list)s for %(name)s "%(object)s".')
% {'list': msg_list[:-2],
'name': force_unicode(changed_object._meta.verbose_name),
'object': force_unicode(changed_object)})
for deleted_object in formset.deleted_objects:
change_message.append(_('Deleted %(name)s "%(object)s".')
% {'name': force_unicode(deleted_object._meta.verbose_name),
'object': force_unicode(deleted_object)})
change_message = ' '.join(change_message)
return change_message or _('No fields changed.')
Upvotes: 1
Reputation: 54000
It's not 'default django'. You are using a contributed django application (the admin) so it's an app like any other (i.e. django-reversion) and to that extent, it doesn't support the feature you have in mind out of the box.
Luckily, the django admin is quite configurable. You could try overwriting the log_change
method of your ModelAdmin
class and making the change_message
more verbose by detecting which fields have changed (by comparing the form values against the database values). Presumably you want this functionality across all apps in your project, so could either write a mixin to support this, or fork the entire admin and hardcode the functionality.
Upvotes: 2