You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DjangoBlog/djangoblog/logentryadmin.py

136 lines
5.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#姜雨菲: 导入Django管理后台核心模块
from django.contrib import admin
#姜雨菲: 导入日志相关常量和模型
from django.contrib.admin.models import DELETION # 表示"删除"操作的常量
from django.contrib.contenttypes.models import ContentType # 内容类型模型,用于关联不同模型
#姜雨菲: 导入URL反向解析和异常处理
from django.urls import reverse, NoReverseMatch
# 导入字符串处理工具
from django.utils.encoding import force_str
# 导入HTML转义工具
from django.utils.html import escape
# 导入安全字符串标记工具用于渲染HTML
from django.utils.safestring import mark_safe
# 导入国际化翻译工具
from django.utils.translation import gettext_lazy as _
class LogEntryAdmin(admin.ModelAdmin):
"""
自定义管理员日志(LogEntry)的管理类
用于在Django admin后台展示和管理系统操作日志
"""
# 列表页的筛选器:按内容类型筛选
list_filter = [
'content_type'
]
# 搜索字段:支持按对象表示和变更消息搜索
search_fields = [
'object_repr', # 对象的字符串表示
'change_message' # 操作变更的描述信息
]
# 列表页中可点击的链接字段
list_display_links = [
'action_time', # 操作时间
'get_change_message', # 变更消息
]
# 列表页展示的字段
list_display = [
'action_time', # 操作时间
'user_link', # 操作用户(带链接)
'content_type', # 操作的内容类型(模型)
'object_link', # 操作的对象(带链接)
'get_change_message', # 变更消息
]
def has_add_permission(self, request):
"""禁用添加权限:不允许手动添加日志记录"""
return False
def has_change_permission(self, request, obj=None):
"""
限制修改权限:
- 仅超级用户或拥有change_logentry权限的用户可查看
- 禁止POST请求即不允许修改日志内容
"""
return (
request.user.is_superuser or
request.user.has_perm('admin.change_logentry')
) and request.method != 'POST'
def has_delete_permission(self, request, obj=None):
"""禁用删除权限:不允许删除日志记录"""
return False
def object_link(self, obj):
"""
生成操作对象的链接(若对象存在)
对于已删除的对象,仅显示文本;对于存在的对象,显示可点击的链接
"""
# 先对对象的字符串表示进行HTML转义防止XSS攻击
object_link = escape(obj.object_repr)
# 获取操作对象的内容类型
content_type = obj.content_type
# 如果不是删除操作且内容类型存在,尝试生成编辑链接
if obj.action_flag != DELETION and content_type is not None:
try:
# 反向解析对象的编辑页面URL
url = reverse(
# 生成admin的URL名称格式app_label_model_change
'admin:{}_{}_change'.format(content_type.app_label,
content_type.model),
args=[obj.object_id] # 传递对象ID作为参数
)
# 生成带链接的HTML
object_link = '<a href="{}">{}</a>'.format(url, object_link)
except NoReverseMatch:
# 若无法解析URL如模型未注册到admin则只显示文本
pass
# 标记为安全字符串允许Django渲染HTML
return mark_safe(object_link)
# 配置列表页字段的排序和显示名称
object_link.admin_order_field = 'object_repr' # 允许按对象表示排序
object_link.short_description = _('object') # 列表页显示的列名(支持国际化)
def user_link(self, obj):
"""生成操作用户的链接(指向用户编辑页面)"""
# 获取用户模型的内容类型
content_type = ContentType.objects.get_for_model(type(obj.user))
# 对用户名进行HTML转义
user_link = escape(force_str(obj.user))
try:
# 反向解析用户编辑页面的URL
url = reverse(
'admin:{}_{}_change'.format(content_type.app_label,
content_type.model),
args=[obj.user.pk] # 传递用户ID作为参数
)
# 生成带链接的HTML
user_link = '<a href="{}">{}</a>'.format(url, user_link)
except NoReverseMatch:
# 若无法解析URL只显示用户名
pass
# 标记为安全字符串允许渲染HTML
return mark_safe(user_link)
# 配置用户链接字段的排序和显示名称
user_link.admin_order_field = 'user' # 允许按用户排序
user_link.short_description = _('user') # 列表页显示的列名(支持国际化)
def get_queryset(self, request):
"""优化查询集预加载content_type减少数据库查询次数"""
queryset = super(LogEntryAdmin, self).get_queryset(request)
return queryset.prefetch_related('content_type') # 使用预加载优化性能
def get_actions(self, request):
"""移除批量删除操作:不允许批量删除日志"""
actions = super(LogEntryAdmin, self).get_actions(request)
if 'delete_selected' in actions:
del actions['delete_selected']
return actions