Django中数据库增删改查询历史查看工具需求(非Admin面板)
嘿,这个需求我刚好做过类似的实现,完全可以让普通用户不用依赖Admin面板就能查看数据库的增删改操作历史,分享下我的方案:
解决方案:面向普通用户的Django数据库操作历史追踪工具
核心思路
通过Django的**信号(Signals)**捕获模型的增、删、改操作,将操作详情记录到专门的历史表中,再开发一个普通用户可访问的视图和页面来展示这些历史记录。全程不需要依赖Admin面板,完全自定义权限和展示样式。
具体实现步骤
1. 创建操作历史模型
首先在你的app下创建一个用于存储操作历史的模型,记录关键信息:
# models.py import json from django.db import models from django.contrib.auth.models import User from django.utils.timezone import now class DBOperationHistory(models.Model): OPERATION_TYPES = ( ('ADD', '新增'), ('UPDATE', '修改'), ('DELETE', '删除'), ) operation_type = models.CharField(max_length=10, choices=OPERATION_TYPES) model_name = models.CharField(max_length=100, verbose_name="模型名称") record_id = models.IntegerField(verbose_name="记录ID") operator = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="操作人" ) operation_time = models.DateTimeField(default=now, verbose_name="操作时间") old_data = models.TextField(null=True, blank=True, verbose_name="旧数据") new_data = models.TextField(null=True, blank=True, verbose_name="新数据") class Meta: ordering = ['-operation_time'] verbose_name = '数据库操作历史' verbose_name_plural = '数据库操作历史' def __str__(self): return f"{self.get_operation_type_display()} - {self.model_name}:{self.record_id}" # 可选:把JSON字符串转成字典,方便模板渲染 def get_old_data_dict(self): return json.loads(self.old_data) if self.old_data else {} def get_new_data_dict(self): return json.loads(self.new_data) if self.new_data else {}
2. 编写信号接收器捕获操作
创建signals.py文件,用Django的post_save和post_delete信号监听所有模型的增删改操作,同时通过中间件获取当前操作人:
信号代码:
# signals.py import json from django.db.models.signals import post_save, post_delete, pre_save from django.dispatch import receiver from .models import DBOperationHistory # 临时存储预保存的旧实例,用于修改操作的对比 _pre_save_old_instances = {} @receiver(pre_save) def store_old_instance(sender, instance, **kwargs): # 排除历史模型本身,避免循环记录 if sender == DBOperationHistory: return if instance.pk: # 存储旧实例的可序列化字段 _pre_save_old_instances[instance.pk] = { k: v for k, v in instance.__dict__.items() if not k.startswith('_') and not callable(v) } @receiver(post_save) def track_save_operation(sender, instance, created, **kwargs): if sender == DBOperationHistory: return operator = getattr(instance, '_current_user', None) model_name = sender.__name__ record_id = instance.pk if created: # 处理新增操作 new_data = json.dumps(instance.get_new_data_dict()) DBOperationHistory.objects.create( operation_type='ADD', model_name=model_name, record_id=record_id, operator=operator, new_data=new_data ) else: # 处理修改操作,对比新旧数据 old_data = _pre_save_old_instances.pop(instance.pk, None) if old_data: old_data_str = json.dumps(old_data) new_data_str = json.dumps(instance.get_new_data_dict()) if old_data_str != new_data_str: DBOperationHistory.objects.create( operation_type='UPDATE', model_name=model_name, record_id=record_id, operator=operator, old_data=old_data_str, new_data=new_data_str ) @receiver(post_delete) def track_delete_operation(sender, instance, **kwargs): if sender == DBOperationHistory: return operator = getattr(instance, '_current_user', None) model_name = sender.__name__ record_id = instance.pk old_data = json.dumps(instance.get_old_data_dict()) DBOperationHistory.objects.create( operation_type='DELETE', model_name=model_name, record_id=record_id, operator=operator, old_data=old_data )
注册信号:
在app的apps.py中注册信号,确保项目启动时加载:
# apps.py from django.apps import AppConfig class YourAppConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'your_app_name' def ready(self): import your_app_name.signals # 替换成你的app名称
3. 中间件获取当前操作人
信号本身无法直接获取请求用户,所以需要写一个中间件把当前用户绑定到模型实例上:
# middleware.py from django.utils.deprecation import MiddlewareMixin class CurrentUserMiddleware(MiddlewareMixin): def process_view(self, request, view_func, view_args, view_kwargs): # 仅对已登录用户生效 if request.user.is_authenticated: # 给后续的模型实例绑定当前用户 setattr(request, '_current_user', request.user) # 可以在这里针对特定模型做处理,或者在视图中手动绑定 return None
然后在settings.py的MIDDLEWARE列表中添加这个中间件:
MIDDLEWARE = [ # ... 其他中间件 'your_app_name.middleware.CurrentUserMiddleware', # 替换成你的app路径 ]
4. 开发普通用户可访问的视图和页面
视图代码:
# views.py from django.shortcuts import render from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator from .models import DBOperationHistory @login_required # 确保只有登录用户能访问 def operation_history(request): # 可以根据需求添加筛选条件,比如只显示当前用户的操作 history_list = DBOperationHistory.objects.all() # 分页处理,每页15条记录 paginator = Paginator(history_list, 15) page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) return render(request, 'operation_history.html', {'page_obj': page_obj})
模板代码(operation_history.html):
{% extends 'base.html' %} <!-- 替换成你的基础模板 --> {% block content %} <div class="container mt-4"> <h2 class="mb-3">数据库操作历史</h2> <table class="table table-hover"> <thead class="table-light"> <tr> <th>操作类型</th> <th>模型</th> <th>记录ID</th> <th>操作人</th> <th>操作时间</th> <th>变更详情</th> </tr> </thead> <tbody> {% for history in page_obj %} <tr> <td> {% if history.operation_type == 'ADD' %} <span class="badge bg-success">{{ history.get_operation_type_display }}</span> {% elif history.operation_type == 'UPDATE' %} <span class="badge bg-warning text-dark">{{ history.get_operation_type_display }}</span> {% else %} <span class="badge bg-danger">{{ history.get_operation_type_display }}</span> {% endif %} </td> <td>{{ history.model_name }}</td> <td>{{ history.record_id }}</td> <td>{{ history.operator.username|default:"未知用户" }}</td> <td>{{ history.operation_time|date:"Y-m-d H:i:s" }}</td> <td> {% if history.operation_type == 'ADD' %} <small><strong>新增内容:</strong></small> <ul> {% for k, v in history.get_new_data_dict.items %} <li>{{ k }}: {{ v }}</li> {% endfor %} </ul> {% elif history.operation_type == 'UPDATE' %} <small><strong>修改前:</strong></small> <ul> {% for k, v in history.get_old_data_dict.items %} {% if history.get_new_data_dict.k != v %} <li>{{ k }}: {{ v }}</li> {% endif %} {% endfor %} </ul> <small><strong>修改后:</strong></small> <ul> {% for k, v in history.get_new_data_dict.items %} {% if history.get_old_data_dict.k != v %} <li>{{ k }}: {{ v }}</li> {% endif %} {% endfor %} </ul> {% else %} <small><strong>删除内容:</strong></small> <ul> {% for k, v in history.get_old_data_dict.items %} <li>{{ k }}: {{ v }}</li> {% endfor %} </ul> {% endif %} </td> </tr> {% empty %} <tr> <td colspan="6" class="text-center py-3">暂无操作记录</td> </tr> {% endfor %} </tbody> </table> <!-- 分页控件 --> <nav aria-label="Page navigation"> <ul class="pagination justify-content-center"> {% if page_obj.has_previous %} <li class="page-item"> <a class="page-link" href="?page=1">首页</a> </li> <li class="page-item"> <a class="page-link" href="?page={{ page_obj.previous_page_number }}">上一页</a> </li> {% endif %} <li class="page-item disabled"> <span class="page-link">第 {{ page_obj.number }} 页 / 共 {{ page_obj.paginator.num_pages }} 页</span> </li> {% if page_obj.has_next %} <li class="page-item"> <a class="page-link" href="?page={{ page_obj.next_page_number }}">下一页</a> </li> <li class="page-item"> <a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">末页</a> </li> {% endif %} </ul> </nav> </div> {% endblock %}
5. 添加URL路由
在app的urls.py中添加访问路由:
# urls.py from django.urls import path from . import views urlpatterns = [ # ... 其他路由 path('operation-history/', views.operation_history, name='operation_history'), ]
额外优化建议
- 筛选功能:可以用
django-filter库添加按模型名称、操作类型、时间范围的筛选,让用户更精准查找记录。 - 权限控制:如果需要普通用户只能查看自己的操作,修改视图中的
history_list为DBOperationHistory.objects.filter(operator=request.user)。 - 性能优化:给
DBOperationHistory的operation_time、model_name字段添加数据库索引;定期归档旧记录到单独的表,避免主表数据过大。 - 友好展示:用JavaScript把JSON数据渲染成更美观的对比格式,比如高亮修改的字段。
内容的提问来源于stack exchange,提问作者Ahmed Wagdi
相关产品推荐
相关产品推荐

