fix: enhance operator log handling and improve M2M field verbose name retrieval

This commit is contained in:
wangruidong
2026-06-02 11:00:02 +08:00
committed by 老广
parent eb13477610
commit 135289f651
2 changed files with 55 additions and 5 deletions

View File

@@ -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

View File

@@ -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(