mirror of
https://github.com/jazzband/django-auditlog.git
synced 2026-04-23 00:04:47 +00:00
113 lines
3.6 KiB
Python
113 lines
3.6 KiB
Python
from django.apps import apps
|
|
from django.contrib.admin import SimpleListFilter
|
|
from django.contrib.admin.filters import DateFieldListFilter
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.db import connection
|
|
from django.db.models import JSONField, Value
|
|
from django.db.models.functions import Cast, Concat
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from auditlog.registry import auditlog
|
|
|
|
|
|
class ShortActorFilter(SimpleListFilter):
|
|
title = _("Actor")
|
|
parameter_name = "actor"
|
|
|
|
def lookups(self, request, model_admin):
|
|
return [("null", _("System")), ("not_null", _("Users"))]
|
|
|
|
def queryset(self, request, queryset):
|
|
value = self.value()
|
|
if value is None:
|
|
return queryset
|
|
if value == "null":
|
|
return queryset.filter(actor__isnull=True)
|
|
return queryset.filter(actor__isnull=False)
|
|
|
|
|
|
class ResourceTypeFilter(SimpleListFilter):
|
|
title = _("Resource Type")
|
|
parameter_name = "resource_type"
|
|
|
|
def lookups(self, request, model_admin):
|
|
tracked_model_names = [
|
|
f"{m._meta.app_label}.{m._meta.model_name}" for m in auditlog.get_models()
|
|
]
|
|
model_name_concat = Concat("app_label", Value("."), "model")
|
|
content_types = ContentType.objects.annotate(
|
|
model_name=model_name_concat,
|
|
).filter(
|
|
model_name__in=tracked_model_names,
|
|
)
|
|
return content_types.order_by("model_name").values_list("id", "model_name")
|
|
|
|
def queryset(self, request, queryset):
|
|
if self.value() is None:
|
|
return queryset
|
|
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 sorted(
|
|
(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(
|
|
**{f"changes_json__{self.value()}__isnull": False}
|
|
)
|
|
|
|
|
|
def get_timestamp_filter():
|
|
"""Returns rangefilter filter class if able or a simple list filter as a fallback."""
|
|
if apps.is_installed("rangefilter"):
|
|
try:
|
|
from rangefilter.filters import DateTimeRangeFilter
|
|
|
|
return DateTimeRangeFilter
|
|
except ImportError:
|
|
pass
|
|
|
|
return DateFieldListFilter
|
|
|
|
|
|
class CIDFilter(SimpleListFilter):
|
|
title = _("Correlation ID")
|
|
parameter_name = "cid"
|
|
|
|
def lookups(self, request, model_admin):
|
|
return []
|
|
|
|
def has_output(self):
|
|
return True
|
|
|
|
def queryset(self, request, queryset):
|
|
if self.value() is None:
|
|
return queryset
|
|
return queryset.filter(cid=self.value())
|