Introduce admin filter by changed field

This commit is contained in:
Alieh Rymašeŭski 2020-04-17 12:52:56 +03:00
parent 5cd55ac38c
commit f58b3d7685
2 changed files with 40 additions and 3 deletions

View file

@ -6,7 +6,7 @@ from django.utils.functional import cached_property
from .models import LogEntry from .models import LogEntry
from .mixins import LogEntryAdminMixin from .mixins import LogEntryAdminMixin
from .filters import ResourceTypeFilter from .filters import ResourceTypeFilter, FieldFilter
class TimeLimitedPaginator(Paginator): class TimeLimitedPaginator(Paginator):
@ -30,7 +30,7 @@ class TimeLimitedPaginator(Paginator):
class LogEntryAdmin(admin.ModelAdmin, LogEntryAdminMixin): class LogEntryAdmin(admin.ModelAdmin, LogEntryAdminMixin):
list_display = ['created', 'resource_url', 'action', 'msg_short', 'user_url'] list_display = ['created', 'resource_url', 'action', 'msg_short', 'user_url']
search_fields = ['timestamp', 'object_repr', 'changes', 'actor__first_name', 'actor__last_name'] search_fields = ['timestamp', 'object_repr', 'changes', 'actor__first_name', 'actor__last_name']
list_filter = ['action', ResourceTypeFilter] list_filter = ['action', ResourceTypeFilter, FieldFilter]
readonly_fields = ['created', 'resource_url', 'action', 'user_url', 'msg'] readonly_fields = ['created', 'resource_url', 'action', 'user_url', 'msg']
fieldsets = [ fieldsets = [
(None, {'fields': ['created', 'user_url', 'resource_url']}), (None, {'fields': ['created', 'user_url', 'resource_url']}),

View file

@ -1,7 +1,9 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import JSONField
from django.db import connection
from django.db.models import Value from django.db.models import Value
from django.db.models.functions import Concat from django.db.models.functions import Concat, Cast
from auditlog.registry import auditlog from auditlog.registry import auditlog
@ -27,3 +29,38 @@ class ResourceTypeFilter(SimpleListFilter):
if self.value() is None: if self.value() is None:
return queryset return queryset
return queryset.filter(content_type_id=self.value()) return queryset.filter(content_type_id=self.value())
class FieldFilter(SimpleListFilter):
title = 'Field'
parameter_name = 'field'
parent = ResourceTypeFilter
def __init__(self, request, *args, **kwargs):
self.target_model = self._get_target_model(request)
super().__init__(request, *args, **kwargs)
def _get_target_model(self, request):
# the parameters consumed by previous filters aren't passed to subsequent filters,
# so we have to look into the request parameters explicitly
content_type_id = request.GET.get(self.parent.parameter_name)
if not content_type_id:
return None
return ContentType.objects.get(id=content_type_id).model_class()
def lookups(self, request, model_admin):
if connection.vendor != 'postgresql':
# filtering inside JSON is PostgreSQL-specific for now
return []
if not self.target_model:
return []
return [(field.name, field.name) for field in self.target_model._meta.fields]
def queryset(self, request, queryset):
if self.value() is None:
return queryset
return (
queryset.annotate(changes_json=Cast("changes", JSONField()))
.filter(**{'changes_json__{}__isnull'.format(self.value()): False})
)