blender-studio/logentry_admin/admin.py

137 lines
4.3 KiB
Python

from django.contrib import admin
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, 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 _
from django.utils.translation import pgettext_lazy
action_names = {
ADDITION: pgettext_lazy('logentry_admin:action_type', 'Addition'),
DELETION: pgettext_lazy('logentry_admin:action_type', 'Deletion'),
CHANGE: pgettext_lazy('logentry_admin:action_type', 'Change'),
}
class ActionListFilter(admin.SimpleListFilter):
title = _('action')
parameter_name = 'action_flag'
def lookups(self, request, model_admin): # noqa: D102
return action_names.items()
def queryset(self, request, queryset): # noqa: D102
if self.value():
queryset = queryset.filter(action_flag=self.value())
return queryset
class LogEntryAdmin(admin.ModelAdmin):
date_hierarchy = 'action_time'
readonly_fields = [f.name for f in LogEntry._meta.fields] + [
'object_link',
'action_description',
'user_link',
'get_change_message',
]
fieldsets = (
(
_('Metadata'),
{'fields': ('action_time', 'user_link', 'action_description', 'object_link',)},
),
(
_('Details'),
{'fields': ('get_change_message', 'content_type', 'object_id', 'object_repr',)},
),
)
list_filter = ['content_type', ActionListFilter]
search_fields = ['object_repr', 'change_message']
list_display_links = [
'action_time',
'get_change_message',
]
list_display = [
'action_time',
'user_link',
'content_type',
'object_link',
'action_description',
'get_change_message',
]
def has_add_permission(self, request): # noqa: D102
return False
def has_change_permission(self, request, obj=None): # noqa: D102
return False
def has_delete_permission(self, request, obj=None): # noqa: D102
return False
def object_link(self, obj): # noqa: D102
object_link = escape(obj.object_repr)
content_type = obj.content_type
if obj.action_flag != DELETION and content_type is not None:
# try returning an actual link instead of object repr string
try:
url = reverse(
'admin:{}_{}_change'.format(content_type.app_label, content_type.model),
args=[obj.object_id],
)
object_link = '<a href="{}">{}</a>'.format(url, object_link)
except NoReverseMatch:
pass
return mark_safe(object_link)
object_link.admin_order_field = 'object_repr'
object_link.short_description = _('object')
def user_link(self, obj): # noqa: D102
content_type = ContentType.objects.get_for_model(type(obj.user))
user_link = escape(force_str(obj.user))
try:
# try returning an actual link instead of object repr string
url = reverse(
'admin:{}_{}_change'.format(content_type.app_label, content_type.model),
args=[obj.user.pk],
)
user_link = '<a href="{}">{}</a>'.format(url, user_link)
except NoReverseMatch:
pass
return mark_safe(user_link)
user_link.admin_order_field = 'user'
user_link.short_description = _('user')
def get_queryset(self, request): # noqa: D102
queryset = super(LogEntryAdmin, self).get_queryset(request)
return queryset.prefetch_related('content_type')
def get_actions(self, request): # noqa: D102
actions = super(LogEntryAdmin, self).get_actions(request)
actions.pop('delete_selected', None)
return actions
def action_description(self, obj): # noqa: D102
return action_names[obj.action_flag]
action_description.short_description = _('action')
def get_change_message(self, obj): # noqa: D102
# TODO: is this still required in newer Django versions?
return obj.change_message
get_change_message.short_description = _('change message')
admin.site.register(LogEntry, LogEntryAdmin)