Skip to content

Commit

Permalink
Fixes netbox-community#14329: Improve diffs for custom fields
Browse files Browse the repository at this point in the history
  • Loading branch information
JCWasmx86 committed Nov 23, 2023
1 parent d52a6d3 commit b8662dd
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 24 deletions.
15 changes: 6 additions & 9 deletions netbox/extras/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.rqworker import get_workers_for_queue
from utilities.templatetags.builtins.filters import render_markdown
from utilities.utils import copy_safe_request, count_related, get_viewname, normalize_querydict, shallow_compare_dict
from utilities.utils import copy_safe_request, count_related, get_viewname, normalize_querydict, deep_compare_dict
from utilities.views import ContentTypePermissionRequiredMixin, register_model_view
from . import filtersets, forms, tables
from .forms.reports import ReportForm
Expand Down Expand Up @@ -661,14 +661,11 @@ def get_extra_context(self, request, instance):
prechange_data = instance.prechange_data

if prechange_data and instance.postchange_data:
diff_added = shallow_compare_dict(
prechange_data or dict(),
instance.postchange_data or dict(),
exclude=['last_updated'],
)
diff_removed = {
x: prechange_data.get(x) for x in diff_added
} if prechange_data else {}
diff_added, diff_removed = deep_compare_dict(prechange_data, instance.postchange_data)
if 'last_updated' in diff_added:
del diff_added['last_updated']
if "last_updated" in diff_removed:
del diff_removed['last_updated']
else:
diff_added = None
diff_removed = None
Expand Down
41 changes: 26 additions & 15 deletions netbox/utilities/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,21 +376,32 @@ def prepare_cloned_fields(instance):
# Return a QueryDict with the parameters
return QueryDict(urlencode(params), mutable=True)


def shallow_compare_dict(source_dict, destination_dict, exclude=tuple()):
"""
Return a new dictionary of the different keys. The values of `destination_dict` are returned. Only the equality of
the first layer of keys/values is checked. `exclude` is a list or tuple of keys to be ignored.
"""
difference = {}

for key, value in destination_dict.items():
if key in exclude:
continue
if source_dict.get(key) != value:
difference[key] = value

return difference
def deep_compare_dict(old, new):
"""
Return a tuple of two dictionaries `(removed_diffs, added_diffs)` in a format
that is compatible with the requirements of `ObjectChangeView`.
"""
added_diffs = {}
removed_diffs = {}

for k in old:
old_data = old[k]
new_data = new[k]
# HACK: Sometimes, there are issues with floating point fields
# where the type of old_data is `float` and new_data is a `str`.
# So we serialize it to a string and compare it.
if str(old_data) != str(new_data):
if isinstance(old_data, dict) and isinstance(new_data, dict):
(sub_added, sub_removed) = deep_compare_dict(old_data, new_data)
if len(sub_removed) > 0:
removed_diffs[k] = sub_removed
if len(sub_added) > 0:
added_diffs[k] = sub_added
else:
removed_diffs[k] = old_data
added_diffs[k] = new_data

return added_diffs, removed_diffs


def flatten_dict(d, prefix='', separator='.'):
Expand Down

0 comments on commit b8662dd

Please sign in to comment.