diff --git a/apps/audits/handler.py b/apps/audits/handler.py index 18d6ddcd9..db12c2792 100644 --- a/apps/audits/handler.py +++ b/apps/audits/handler.py @@ -1,5 +1,6 @@ import json from datetime import datetime +from uuid import UUID from django.core.cache import cache from django.db import transaction @@ -42,13 +43,33 @@ class OperatorLogHandler(metaclass=Singleton): value2 = as_current_tz(value2).strftime('%Y-%m-%d %H:%M:%S') return value1, value2 + @classmethod + def _normalize_diff_value(cls, value): + if isinstance(value, datetime): + return as_current_tz(value).strftime('%Y-%m-%d %H:%M:%S') + if isinstance(value, UUID): + return str(value) + if isinstance(value, dict): + return {k: cls._normalize_diff_value(v) for k, v in value.items()} + if isinstance(value, (list, tuple, set)): + return [cls._normalize_diff_value(v) for v in value] + return value + + @classmethod + def _value_equal_for_diff(cls, left, right): + left = cls._normalize_diff_value(left) + right = cls._normalize_diff_value(right) + if left == right: + return True + + return str(left) == str(right) + def _look_for_two_dict_change(self, left_dict, right_dict): # 以右边的字典为基础 before, after = {}, {} for key, value in right_dict.items(): pre_value = left_dict.get(key, '') - pre_value, value = self._consistent_type_to_str(pre_value, value) - if sorted(str(value)) == sorted(str(pre_value)): + if self._value_equal_for_diff(pre_value, value): continue before[key] = pre_value after[key] = value diff --git a/apps/audits/signal_handlers/operate_log.py b/apps/audits/signal_handlers/operate_log.py index 6f875afb2..18b949249 100644 --- a/apps/audits/signal_handlers/operate_log.py +++ b/apps/audits/signal_handlers/operate_log.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # import uuid +from itertools import chain from django.apps import apps from django.db.models.signals import ( @@ -26,6 +27,28 @@ M2M_ACTION = { } +def _get_m2m_field_verbose_name(instance, model, sender): + opts = instance._meta + for f in chain(opts.many_to_many, opts.related_objects): + related_model = getattr(f, 'related_model', None) + if related_model != model: + continue + + through = None + remote_field = getattr(f, 'remote_field', None) + if remote_field is not None: + through = getattr(remote_field, 'through', None) + if through is None: + through = getattr(f, 'through', None) + + if through is not sender: + continue + + verbose_name = getattr(f, 'verbose_name', None) or model._meta.verbose_name + return str(verbose_name) + return str(model._meta.verbose_name) + + @receiver(m2m_changed) def on_m2m_changed(sender, action, instance, reverse, model, pk_set, **kwargs): if action not in M2M_ACTION: @@ -39,15 +62,21 @@ def on_m2m_changed(sender, action, instance, reverse, model, pk_set, **kwargs): instance, include_model_fields=False, include_related_fields=[model] ) - instance_id = current_instance.get('id') + instance_id_data = current_instance.get('id', {}) + if isinstance(instance_id_data, dict): + instance_id = instance_id_data.get('value') + else: + instance_id = instance_id_data log_id, before_instance = get_instance_dict_from_cache(instance_id) - field_name = str(model._meta.verbose_name) + field_name = _get_m2m_field_verbose_name(instance, model, sender) pk_set = pk_set or {} objs = model.objects.filter(pk__in=pk_set) objs_display = [str(o) for o in objs] action = M2M_ACTION[action] changed_field = current_instance.get(field_name, {}) + if not changed_field and field_name != str(model._meta.verbose_name): + changed_field = current_instance.get(str(model._meta.verbose_name), {}) changed_value = changed_field.get('value', []) after, before, before_value = None, None, None @@ -63,7 +92,7 @@ def on_m2m_changed(sender, action, instance, reverse, model, pk_set, **kwargs): before_change_field['value'] = before_value before = {field_name: before_change_field} - if sorted(str(before)) == sorted(str(after)): + if before == after: return create_or_update_operate_log(