在開發測試平臺的時候,雖然對某些關鍵功能做了許可權設定,但畢竟是公司多人使用,有些資料的配置可能不小心被他人修改但未告知其他使用者,造成了諸多不便。所以決定開發一個操作歷史表,可以方便檢視資料地改動。
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='' # 自定義訊息,可以放入修改之後的資料
)
實現效果