在Django Admin中记录模型更新的差异日志
Django Admin 专属操作日志(带字段差异对比)
既然只需要记录Admin后台的操作,直接从Admin层面入手就好,不用挨个改模型的save方法。下面是一套可复用的方案:
1. 先建日志模型
用来存储操作记录,包含核心信息:
from django.db import models from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey class AdminOperationLog(models.Model): OPERATION_TYPE = ( ('create', '新增'), ('update', '修改'), ('delete', '删除'), ) user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, verbose_name='操作人') content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.CharField(max_length=255) content_object = GenericForeignKey('content_type', 'object_id') operation_type = models.CharField(max_length=10, choices=OPERATION_TYPE, verbose_name='操作类型') diff_data = models.JSONField(verbose_name='字段差异', blank=True, null=True) operated_at = models.DateTimeField(auto_now_add=True, verbose_name='操作时间') class Meta: verbose_name = 'Admin操作日志' verbose_name_plural = verbose_name ordering = ['-operated_at']
2. 写一个通用的Admin基类
所有需要日志的模型Admin都继承这个基类,不用重复写逻辑:
from django.contrib import admin from .models import AdminOperationLog class LoggableAdminMixin(admin.ModelAdmin): # 可以在这里定义不需要记录的字段,比如自动更新的时间戳、主键等 EXCLUDE_FIELDS = ['id', 'created_at', 'updated_at'] def get_old_object(self, obj): # 获取数据库中当前对象的旧数据 return self.model.objects.get(pk=obj.pk) def get_field_diff(self, old_obj, new_obj): # 对比新旧对象的字段差异 diff = {} for field in self.model._meta.fields: field_name = field.name if field_name in self.EXCLUDE_FIELDS: continue old_value = getattr(old_obj, field_name) new_value = getattr(new_obj, field_name) # 处理外键:用__str__展示更友好 if field.is_relation: old_value = str(old_value) if old_value else None new_value = str(new_value) if new_value else None if old_value != new_value: diff[field_name] = { 'old': old_value, 'new': new_value } return diff def save_model(self, request, obj, form, change): # 先执行原保存逻辑 super().save_model(request, obj, form, change) # 记录日志 if change: # 修改操作:对比新旧数据 old_obj = self.get_old_object(obj) diff_data = self.get_field_diff(old_obj, obj) if diff_data: # 只有有变化才记录 AdminOperationLog.objects.create( user=request.user, content_object=obj, operation_type='update', diff_data=diff_data ) else: # 新增操作:记录所有字段值 field_data = {} for field in self.model._meta.fields: field_name = field.name if field_name in self.EXCLUDE_FIELDS: continue value = getattr(obj, field_name) if field.is_relation: value = str(value) if value else None field_data[field_name] = value AdminOperationLog.objects.create( user=request.user, content_object=obj, operation_type='create', diff_data=field_data ) def delete_model(self, request, obj): # 先记录删除日志 field_data = {} for field in self.model._meta.fields: field_name = field.name if field_name in self.EXCLUDE_FIELDS: continue value = getattr(obj, field_name) if field.is_relation: value = str(value) if value else None field_data[field_name] = value AdminOperationLog.objects.create( user=request.user, content_object=obj, operation_type='delete', diff_data=field_data ) # 再执行原删除逻辑 super().delete_model(request, obj)
3. 给需要日志的模型Admin加继承
比如你的Book模型,只需要让它的Admin继承这个Mixin:
from django.contrib import admin from .models import Book from .admin_mixins import LoggableAdminMixin @admin.register(Book) class BookAdmin(LoggableAdminMixin, admin.ModelAdmin): # 你的原有配置 list_display = ['title', 'author', 'price']
额外说明
- 可以根据需求调整
EXCLUDE_FIELDS,排除不需要记录的字段 - 如果需要处理多对多字段,可以在
save_model里额外处理,对比多对多的关联对象变化 - 日志模型里的
diff_data用JSONField存储,方便后续展示差异(比如在Admin里做一个友好的差异视图) - 这个方案只针对Admin后台的操作,API或其他代码调用save/delete不会触发日志,完全符合你的需求
内容的提问来源于stack exchange,提问作者user10611897
相关产品推荐
相关产品推荐

