在开发测试平台的时候,虽然对某些关键功能做了权限设置,但毕竟是公司多人使用,有些数据的配置可能不小心被他人修改但未告知其他使用者,造成了诸多不便。所以决定开发一个操作历史表,可以方便查看数据地改动。
LogEntry是在后台开发中经常用到的模块,它在admin是默认开启的。
可以使用LogEntry模块记录所有用户的操作记录。一方面可以用来监督,另一方面可以用来做回滚。
使用LogEntry
ModelAdmin本身就有日志记录功能。当新建一个实体(Post、Category、Tag)时,ModelAdmin会创建一条变更日志记录。当修改一条内容时,ModelAdmin又会调用LogEntry来创建一条日志,记录这个变更。
ModelAdmin内部提供了两个方法,分别是log_addition和log_change。
log_addition记录新增日志。
log_change记录变更日志。
log_deletion记录删除日志。
我们可以看它们的定义来学习LogEntry模块
代码位置:Lib\site-packages\django\contrib\admin\options.py
def log_addition(self, request, object, message):
"""
Log that an object has been successfully added.
The default implementation creates an admin LogEntry object.
"""
from django.contrib.admin.models import LogEntry, ADDITION
return LogEntry.objects.log_action(
user_id=request.user.pk,
content_type_id=get_content_type_for_model(object).pk,
object_id=object.pk,
object_repr=str(object),
action_flag=ADDITION,
change_message=message,
)
def log_change(self, request, object, message):
"""
Log that an object has been successfully changed.
The default implementation creates an admin LogEntry object.
"""
from django.contrib.admin.models import LogEntry, CHANGE
return LogEntry.objects.log_action(
user_id=request.user.pk,
content_type_id=get_content_type_for_model(object).pk,
object_id=object.pk,
object_repr=str(object),
action_flag=CHANGE,
change_message=message,
)
def log_deletion(self, request, object, object_repr):
"""
Log that an object will be deleted. Note that this method must be
called before the deletion.
The default implementation creates an admin LogEntry object.
"""
from django.contrib.admin.models import LogEntry, DELETION
return LogEntry.objects.log_action(
user_id=request.user.pk,
content_type_id=get_content_type_for_model(object).pk,
object_id=object.pk,
object_repr=object_repr,
action_flag=DELETION,
)
从以上代码可以看出:这两个方法都调用了LogEntry.objects.log_action方法,只是参数略有不同,可以看到,如果需要自定义变更记录的话,只需要传递对应的参数即可。以下简要介绍一下这些参数。
- user_id:当前用户id
- content_type_id:要保存内容的类型,上面的代码中使用的是get_.content_type_for_model方法拿到对应Model的类型id。这可以简单理解为ContentType为每个Model定义了一个类型id
- object_id:记录变更实例的id
- object_repr:实例的展示名称,可以简单理解为我们定义的__str__所返回的内容
- action_flag:操作标记。admin的Model里面定义了几种基础的标记: ADDITION、CHANGE和DELETION。它用来标记当前参数是数据变更、新增,还是删除。
- change_message:这是记录的消息,可以自行定义。我们可以把新添加的内容放进去(必要时可以通过这里来恢复),也可以把新旧内容的区别放进去。
理解了这几个参数,如果遇到类似的需求,就能直接使用Django现成的工具来完成了。
查询某个对象的变更
上面我们知道如何记录某个对象的变更日志了,那么问题来了,如何在询已经记录的变更呢?
其实这是简单的Model查询问题。假设我们记录的对象是Post的操作,现在来获取Post中id为1的所有变更日志,大概代码如下:
from django.contrib.admin.models import LogEntry, CHANGE
from django.contrib.admin.options import get_content_type_for_model
post = Post.objects.get(id=1)
log_entries = LogEntry.objects.filter(
content_type_id=get_content_type_for_model(post).id,
object_id=post.id)
这样我们就拿到了id为1的所有变更记录了。
在admin页面上查看操作日志
我们既知道如何记录变更日志,也知道如何获取变更日志,那么如何才能够在admin后台方便地查看操作日志呢?
新增如下配置(admin.py):
#最上面增加import
from django.contrib.admin.models import LogEntry
#文件最下方增加
@adnin.register(LogEntry, site=custom_site)
class LogEntryAdmin(admin.ModelAdmin):
list_display = ['object_repr','object_ id','action_flag','user','change_message']
这样就可以看到所有的变更记录了。如下图所示:
实战
虽然操作历史显示出来了,但是在django自带的admin后台才能看到,不是所有人都有进入admin后台权限,能不能有一种方法能在前端展示出来让每个人都可以查看呢?
前端代码:
<table class="table table-bordered" style="width: 60%;margin-left: 20%;text-align: center">
<caption style="text-align: center">
<span style="font-size: large;color: black">项目:【<span style="color: pink">{{ client.name }}</span>】的操作记录: </span>
</caption>
<thead>
<tr style="background-color: #cdd8e5">
<th style="width: 100px;text-align: center">操作时间</th>
<th style="width: 200px;text-align: center">操作对象</th>
<th style="width: 50px;text-align: center">对象id</th>
<th style="width: 100px;text-align: center">动作标志</th>
<th style="width: 100px;text-align: center">操作用户</th>
<th style="width: 300px;text-align: center">修改内容</th>
</tr>
</thead>
<tbody>
{% for i in objects %}
<tr>
<td>{{ i.action_time }}</td>
<td>{{ i.object_repr }}</td>
<td>{{ i.object_id }}</td>
<td>{{ i.get_action_flag_display }}</td>
<td>{{ i.user }}</td>
<td>{{ i.change_message }}</td>
</tr> {% endfor %}
</tbody>
</table>
这里有个坑,如果就利用{{i.action_flag}}获取操作标记的话,得到的是索引值,经过stackoverflow.com查阅,需要利用Django-doc(用您要在其文本表示形式中“翻译”的字段名称替换)来获取相应值,因此:get_fieldname_display,所以这里要写成{{ i.get_action_flag_display }}
后端代码(views.py):
# 导入所需的包
from django.contrib.admin.models import LogEntry, CHANGE, ADDITION, DELETION
from django.contrib.admin.options import get_content_type_for_model
res = dict()
res['objects'] = LogEntry.objects.all() # 获取到所有操作历史
# 只需要调用LogEntry.objects.log_action方法带入所需参数即可
LogEntry.objects.log_action(
user_id=request.user.pk, # 操作用户的id
content_type_id=get_content_type_for_model(object).pk, # 对应数据库Model的id
object_id=object.pk, # 操作对象的id
object_repr=object_repr, # 操作对象的名字
action_flag=DELETION, # ADDITION、CHANGE和DELETION三种方式选择合适的
change_message='' # 自定义消息,可以放入修改之后的数据
)
实现效果