-
Notifications
You must be signed in to change notification settings - Fork 414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow cascade deletion of auditlog entries #556
Allow cascade deletion of auditlog entries #556
Conversation
Codecov Report
@@ Coverage Diff @@
## master #556 +/- ##
==========================================
+ Coverage 94.93% 94.96% +0.03%
==========================================
Files 31 31
Lines 988 994 +6
==========================================
+ Hits 938 944 +6
Misses 50 50
... and 1 file with indirect coverage changes 📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
e73eb30
to
344208c
Compare
@hramezani, we don't have a structure for testing this case with automated tests, and I don't have an elegant way to test this. If you can think of a good way of testing this, please add a test. Otherwise I'd suggest merging this change without a test. |
auditlog/admin.py
Outdated
if request.resolver_match.url_name not in [ | ||
pattern.name for pattern in self.urls if pattern.name | ||
]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aleh-rymasheuski Could you please explain more about what is happening here?
Also, I think you can move the [pattern.name for pattern in self.urls if pattern.name]
out of method and define a class attr to don't repeat the loop in each call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
request.resolver_match
is the information about the matched URL handler:
(Pdb) request.resolver_match
ResolverMatch(func=django.contrib.admin.options.changelist_view, args=(), kwargs={}, url_name=auditlog_logentry_changelist, app_names=['admin'], namespaces=['admin'], route=admin/auditlog/logentry/)
Its url_name
is the symbolic name of the route, e.g. 'auditlog_logentry_changelist'
.
self.urls
is the relative routes defined in the current admin class:
(Pdb) pp self.urls
[<URLPattern '' [name='auditlog_logentry_changelist']>,
<URLPattern 'add/' [name='auditlog_logentry_add']>,
<URLPattern '<path:object_id>/history/' [name='auditlog_logentry_history']>,
<URLPattern '<path:object_id>/delete/' [name='auditlog_logentry_delete']>,
<URLPattern '<path:object_id>/change/' [name='auditlog_logentry_change']>,
<URLPattern '<path:object_id>/'>]
This check basically denies the delete permission if the request is handled by LogEntryAdmin
, and allows it otherwise.
Thanks @aleh-rymasheuski for this patch 🙏 I think we can test it like: diff --git a/auditlog_tests/tests.py b/auditlog_tests/tests.py
index 12a4725..8824780 100644
--- a/auditlog_tests/tests.py
+++ b/auditlog_tests/tests.py
@@ -19,7 +19,7 @@ from django.core import management
from django.db import models
from django.db.models.signals import pre_save
from django.test import RequestFactory, TestCase, override_settings
-from django.urls import reverse
+from django.urls import resolve, reverse
from django.utils import dateformat, formats
from django.utils import timezone as django_timezone
from django.utils.encoding import smart_str
@@ -1555,6 +1555,9 @@ class DiffMsgTest(TestCase):
super().setUp()
self.site = AdminSite()
self.admin = LogEntryAdmin(LogEntry, self.site)
+ self.user = User.objects.create_user(
+ username="test", email="test@example.com", password="top_secret"
+ )
def _create_log_entry(self, action, changes):
return LogEntry.objects.log_create(
@@ -1711,6 +1714,13 @@ class DiffMsgTest(TestCase):
):
self.assertEqual(self.admin.field_verbose_name(log_entry, "actor"), "actor")
+ def test_has_delete_permission(self):
+ request = RequestFactory().get("/simplemodel/1/")
+ request.resolver_match = resolve("/simplemodel/1/")
+ request.user = self.user
+
+ self.assertFalse(self.admin.has_delete_permission(request))
+
class NoDeleteHistoryTest(TestCase):
def test_delete_related(self): |
344208c
to
c08c26b
Compare
@hramezani, I added a test based on your suggestion. |
auditlog/admin.py
Outdated
@@ -40,6 +40,11 @@ def has_change_permission(self, request, obj=None): | |||
return False | |||
|
|||
def has_delete_permission(self, request, obj=None): | |||
if request.resolver_match and request.resolver_match.url_name not in [ | |||
pattern.name for pattern in self.urls if pattern.name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't we move the admin urls patterns list creation out of this method to not calculate it every time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure when in the lifecycle of the admin self.urls
is available, so I'll just move this expression into a cached property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
* Allow cascade deletion of auditlog entries * Cache iteration over self.urls
Resolves #493.
This pull request allows cascade deletion of log entries, but still disallows direct deletion through the admin.