from django.contrib import admin
from django.contrib.admin.models import DELETION
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse, NoReverseMatch
from django.utils.encoding import force_str
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
class LogEntryAdmin(admin.ModelAdmin):
"""Django管理员日志条目的自定义管理界面"""
# 列表页过滤器配置:按内容类型过滤
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):
"""修改权限控制:只允许超级用户或具有特定权限的用户查看(不允许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):
"""生成对象链接的显示"""
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:{}_{}_change'.format(content_type.app_label,
content_type.model),
args=[obj.object_id]
)
# 创建HTML链接
object_link = '{}'.format(url, object_link)
except NoReverseMatch:
# 如果无法生成URL,保持原样
pass
return mark_safe(object_link) # 标记为安全HTML
# 设置对象链接字段的排序和显示名称
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))
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]
)
# 创建HTML链接
user_link = '{}'.format(url, user_link)
except NoReverseMatch:
# 如果无法生成URL,保持原样
pass
return mark_safe(user_link) # 标记为安全HTML
# 设置用户链接字段的排序和显示名称
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