mirror of
https://github.com/jazzband/django-auditlog.git
synced 2026-03-16 22:20:26 +00:00
structured_search
This commit is contained in:
parent
5da57d08d2
commit
b9faed9b7e
1 changed files with 59 additions and 45 deletions
|
|
@ -1,5 +1,6 @@
|
|||
from functools import cached_property
|
||||
|
||||
import re
|
||||
from django.apps import apps
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth import get_user_model
|
||||
|
|
@ -11,6 +12,10 @@ from auditlog.filters import CIDFilter, ResourceTypeFilter
|
|||
from auditlog.mixins import LogEntryAdminMixin
|
||||
from auditlog.models import LogEntry
|
||||
|
||||
STRUCTURED_SEARCH_PATTERN = re.compile(
|
||||
r"^(?P<model_name>[A-Za-z_][A-Za-z0-9_]*):(?P<id>[1-9][0-9]*)$"
|
||||
)
|
||||
|
||||
|
||||
@admin.register(LogEntry)
|
||||
class LogEntryAdmin(admin.ModelAdmin, LogEntryAdminMixin):
|
||||
|
|
@ -61,54 +66,63 @@ class LogEntryAdmin(admin.ModelAdmin, LogEntryAdminMixin):
|
|||
self.request = request
|
||||
queryset = super().get_queryset(request=request)
|
||||
|
||||
# Check for the `ss` parameter for structured search
|
||||
structured_search = request.GET.get("ss")
|
||||
if structured_search:
|
||||
# Parse structured search term as 'ModelName:id'
|
||||
try:
|
||||
model_name, object_id = structured_search.split(":")
|
||||
object_id = int(object_id)
|
||||
except (ValueError, TypeError):
|
||||
# If the format is incorrect, return an empty queryset and show a message
|
||||
if not getattr(request, "_message_shown", False):
|
||||
self.message_user(
|
||||
request,
|
||||
"Structured search format must be 'ModelName:id'.",
|
||||
level="warning",
|
||||
)
|
||||
request._message_shown = True
|
||||
return queryset.none()
|
||||
# Custom tampering with Django Admin search.
|
||||
# We support a structured search here to allow
|
||||
# searching for log entries related to a specific object.
|
||||
search = request.GET.get("q")
|
||||
if search:
|
||||
match = STRUCTURED_SEARCH_PATTERN.match(search)
|
||||
if match:
|
||||
try:
|
||||
model_name = match.group("model_name")
|
||||
object_id = int(match.group("id"))
|
||||
except (ValueError, TypeError):
|
||||
# If the format is incorrect, return an empty queryset and show a message
|
||||
if not getattr(request, "_message_shown", False):
|
||||
self.message_user(
|
||||
request,
|
||||
"Structured search format must be 'ModelName:id'.",
|
||||
level="warning",
|
||||
)
|
||||
request._message_shown = True
|
||||
return queryset.none()
|
||||
|
||||
# Attempt to retrieve the specified model
|
||||
try:
|
||||
model = apps.get_model(app_label="api", model_name=model_name)
|
||||
if not model:
|
||||
raise LookupError
|
||||
except LookupError:
|
||||
if not getattr(request, "_message_shown", False):
|
||||
self.message_user(
|
||||
request,
|
||||
f"Model '{model_name}' does not exist.",
|
||||
level="warning",
|
||||
# Attempt to retrieve the specified model
|
||||
try:
|
||||
model = apps.get_model(
|
||||
app_label="api", model_name=model_name
|
||||
)
|
||||
request._message_shown = True
|
||||
return queryset.none()
|
||||
if not model:
|
||||
raise LookupError
|
||||
except LookupError:
|
||||
if not getattr(request, "_message_shown", False):
|
||||
self.message_user(
|
||||
request,
|
||||
f"Model '{model_name}' does not exist.",
|
||||
level="warning",
|
||||
)
|
||||
request._message_shown = True
|
||||
return queryset.none()
|
||||
|
||||
# Attempt to retrieve the object and filter log entries
|
||||
try:
|
||||
model.objects.only("id").get(pk=object_id) # Lookup.
|
||||
content_type = ContentType.objects.get_for_model(model)
|
||||
queryset = queryset.filter(
|
||||
content_type=content_type, object_id=object_id
|
||||
)
|
||||
except ObjectDoesNotExist:
|
||||
if not getattr(request, "_message_shown", False):
|
||||
self.message_user(
|
||||
request,
|
||||
f"{model_name} instance with ID {object_id} does not exist.",
|
||||
level="warning",
|
||||
# Attempt to retrieve the object and filter log entries
|
||||
try:
|
||||
model.objects.only("id").get(pk=object_id) # Lookup.
|
||||
content_type = ContentType.objects.get_for_model(model)
|
||||
queryset = queryset.filter(
|
||||
content_type=content_type, object_id=object_id
|
||||
)
|
||||
request._message_shown = True
|
||||
return queryset.none()
|
||||
# We need to remove `q` from the request to prevent Django
|
||||
# from trying to search again with structured search query.
|
||||
request.GET = request.GET.copy()
|
||||
request.GET.pop("q", None)
|
||||
except ObjectDoesNotExist:
|
||||
if not getattr(request, "_message_shown", False):
|
||||
self.message_user(
|
||||
request,
|
||||
f"{model_name} instance with ID {object_id} does not exist.",
|
||||
level="warning",
|
||||
)
|
||||
request._message_shown = True
|
||||
return queryset.none()
|
||||
|
||||
return queryset # Return filtered or default queryset based on the presence of `ss`
|
||||
|
|
|
|||
Loading…
Reference in a new issue