# Exports
__all__ = (
"get_field_changes",
"FieldChange",
)
# Functions
[docs]def get_field_changes(form, model, record=None):
"""Get the changed fields for a given form and record.
:param form: The validated form instance.
:param model: The model class represented by the record.
:param record: The current record instance. It is safe to pass ``None``.
:rtype: list[superdjango.db.history.utils.FieldChange]
:returns: A list pf changed fields when record is not ``None``. Otherwise, an empty list.
"""
# It's possible at runtime that a form instance is None. If so, there's nothing to compare and we don't want to
# raise an error because we will assume it's intentional.
if form is None:
return list()
# We might not have received a record, for example if this is a create operation. So again, don't raise an error.
if record is None:
return list()
# Get the existing data.
old_record = model.objects.get(pk=record.pk)
# Compare old data with submitted data.
changes = list()
# noinspection PyProtectedMember
for field in model._meta.get_fields():
if field.name in form.cleaned_data:
old_value = getattr(old_record, field.name)
if old_value != form.cleaned_data[field.name]:
change = FieldChange(field.name, form.cleaned_data[field.name], old_value, label=field.verbose_name)
changes.append(change)
# Return the change instances.
return changes
# Classes
[docs]class FieldChange(object):
"""Wraps the change that has occurred to a given field."""
[docs] def __init__(self, field_name, new_value, old_value, label=None):
"""Initialize a field change.
:param field_name: The name of the field that was changed.
:type field_name: str
:param new_value: The new value, e.g. from the cleaned data of a form.
:param old_value: The existing value from the database prior to save.
:param label: The field label (verbose name).
:type label: str
"""
self.field_label = label or field_name.replace("_", " ")
self.field_name = field_name
self.new_value = new_value
self.old_value = old_value
def __repr__(self):
return "<%s %s: %s -> %s>" % (self.__class__.__name__, self.field_name, self.old_value, self.new_value)
def __str__(self):
return "%s: %s -> %s" % (self.field_label, self.old_value, self.new_value)