Cache model permission codenames in PermissionHelper

pull/8702/head
Tidiane Dia 2022-06-07 16:55:05 +01:00 zatwierdzone przez Dan Braghis
rodzic fd7ef0a68a
commit cb4017c649
5 zmienionych plików z 33 dodań i 5 usunięć

Wyświetl plik

@ -38,6 +38,7 @@ Changelog
* Add `add_to_admin_menu` option for ModelAdmin (Oliver Parker)
* Implement Fuzzy matching for Elasticsearch (Nick Smith)
* Rename `Page.get_latest_revision_as_page` to `Page.get_latest_revision_as_object` (Sage Abdullah)
* Cache model permission codenames in PermissionHelper (Tidiane Dia)
* Fix: Typo in `ResumeWorkflowActionFormatter` message (Stefan Hammer)
* Fix: Throw a meaningful error when saving an image to an unrecognised image format (Christian Franke)
* Fix: Remove extra padding for headers with breadcrumbs on mobile viewport (Steven Steinwand)

Wyświetl plik

@ -46,6 +46,7 @@ When using a queryset to render a list of images, you can now use the ``prefetch
* Added [multi-site support](api_filtering_pages_by_site) to the API (Sævar Öfjörð Magnússon)
* Add `add_to_admin_menu` option for ModelAdmin (Oliver Parker)
* Implement [Fuzzy matching](fuzzy_matching) for Elasticsearch (Nick Smith)
* Cache model permission codenames in `PermissionHelper` (Tidiane Dia)
### Bug fixes

Wyświetl plik

@ -1,6 +1,9 @@
from functools import lru_cache
from django.contrib.auth import get_permission_codename
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.utils.functional import cached_property
from wagtail.models import Page, UserPagePermissionsProxy
@ -28,6 +31,14 @@ class PermissionHelper:
content_type__model=self.opts.model_name,
)
@cached_property
def all_permission_codenames(self):
return list(
self.get_all_model_permissions()
.values_list("codename", flat=True)
.distinct()
)
def get_perm_codename(self, action):
return get_permission_codename(action, self.opts)
@ -39,13 +50,14 @@ class PermissionHelper:
return user.has_perm("%s.%s" % (self.opts.app_label, perm_codename))
@lru_cache(maxsize=128)
def user_has_any_permissions(self, user):
"""
Return a boolean to indicate whether `user` has any model-wide
permissions
"""
for perm in self.get_all_model_permissions().values("codename"):
if self.user_has_specific_permission(user, perm["codename"]):
for perm_codename in self.all_permission_codenames:
if self.user_has_specific_permission(user, perm_codename):
return True
return False

Wyświetl plik

@ -11,7 +11,9 @@ from django.utils.timezone import make_aware
from openpyxl import load_workbook
from wagtail.admin.admin_url_finder import AdminURLFinder
from wagtail.admin.models import Admin
from wagtail.admin.panels import FieldPanel, TabbedInterface
from wagtail.contrib.modeladmin.helpers.permission import PermissionHelper
from wagtail.contrib.modeladmin.helpers.search import DjangoORMSearchHandler
from wagtail.images.models import Image
from wagtail.images.tests.utils import get_test_image_file
@ -917,6 +919,18 @@ class TestEditorAccess(TestCase, WagtailTestUtils):
response = self.client.post("/admin/modeladmintest/book/delete/2/")
self.assertRedirects(response, "/admin/")
def test_permission_helper(self):
permission_helper = PermissionHelper(Admin)
# Populate user permissions cache
with self.assertNumQueries(2):
self.assertTrue(self.user.has_perm("wagtailadmin.access_admin"))
with self.assertNumQueries(1):
# Only one query - to retrieve the model's codenames - should be performed.
self.assertTrue(permission_helper.user_has_any_permissions(self.user))
self.assertTrue(permission_helper.user_can_list(self.user))
class TestHistoryView(TestCase, WagtailTestUtils):
fixtures = ["modeladmintest_test.json"]

Wyświetl plik

@ -266,7 +266,7 @@ class TestImageIndexView(TestCase, WagtailTestUtils):
def test_num_queries(self):
# Initial number of queries.
with self.assertNumQueries(23):
with self.assertNumQueries(13):
self.get()
# Add 5 images.
@ -276,11 +276,11 @@ class TestImageIndexView(TestCase, WagtailTestUtils):
file=get_test_image_file(size=(1, 1)),
)
with self.assertNumQueries(45):
with self.assertNumQueries(35):
# The renditions needed don't exist yet. We have 20 = 5 * 4 + 2 additional queries.
self.get()
with self.assertNumQueries(25):
with self.assertNumQueries(15):
# No extra additional queries since renditions exist and are saved in
# the prefetched objects cache.
self.get()