Skip to content

Instantly share code, notes, and snippets.

@marteinn
Created July 5, 2023 03:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marteinn/813f1f3f424d0ccde9fa775bfd4a2610 to your computer and use it in GitHub Desktop.
Save marteinn/813f1f3f424d0ccde9fa775bfd4a2610 to your computer and use it in GitHub Desktop.
Generate a LogEntry every time a user is inspecting a object in the django admin.
import json
import re
from datetime import timedelta
from django.contrib.admin.models import ADDITION, LogEntry
from django.contrib.contenttypes.models import ContentType
from django.utils import timezone
admin_change_path_match = r"^admin:([a-z_]*)_([a-z]*)_change$"
class AdminObjectInspectionMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if not request.resolver_match:
return response
view_name = request.resolver_match.view_name
match = re.search(admin_change_path_match, view_name)
if not match:
return response
app_name = match.group(1)
model_name = match.group(2)
object_id = request.resolver_match.kwargs["object_id"]
content_type = ContentType.objects.get(app_label=app_name, model=model_name)
instance = content_type.get_object_for_this_type(pk=object_id)
user = request.user
# If similar LogEntry is more recent than 15 minutes exist, exit
if LogEntry.objects.filter(
user=user,
change_message__contains="viewed_in_admin",
content_type=content_type,
object_id=object_id,
action_time__gt=timezone.now() - timedelta(minutes=15),
).exists():
return response
message = [{"added": {"message": "viewed_in_admin"}}]
LogEntry.objects.create(
user=user,
change_message=json.dumps(message),
action_flag=ADDITION,
content_type=content_type,
object_id=object_id,
object_repr=str(instance),
)
return response
from django.contrib.admin.models import LogEntry
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from django.urls import reverse
User = get_user_model()
class AdminObjectInspectionTestCase(TestCase):
def setUp(self):
self.user = User.objects.create_superuser(
username="admin", password="password", email="test@test.test"
)
self.client.login(username="admin", password="password")
def test_log_entry_are_created_on_inspection(self):
self.assertEqual(LogEntry.objects.count(), 0)
viewed_user = User.objects.create_superuser(
username="viewed_user", password="password", email="test+2@test.test"
)
user_type = ContentType.objects.get(app_label="auth", model="user")
self.client.get(
reverse("admin:auth_user_change", args=[viewed_user.pk]),
)
self.assertEqual(LogEntry.objects.count(), 1)
log_entry = LogEntry.objects.all().order_by("-action_time")[0]
self.assertEqual(log_entry.user, self.user)
self.assertEqual(log_entry.content_type, user_type)
self.assertEqual(log_entry.object_id, str(viewed_user.pk))
self.assertTrue("viewed_in_admin" in log_entry.change_message)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment