diff --git a/wagtail/test/snippets/apps.py b/wagtail/test/snippets/apps.py index 13e6f1bc5f..2e21040227 100644 --- a/wagtail/test/snippets/apps.py +++ b/wagtail/test/snippets/apps.py @@ -7,3 +7,13 @@ class WagtailSnippetsTestsAppConfig(AppConfig): name = "wagtail.test.snippets" label = "snippetstests" verbose_name = _("Wagtail snippets tests") + + def ready(self): + # Test registration of permission order within the group permissions view, + # as per https://docs.wagtail.org/en/stable/extending/customizing_group_views.html#customizing-the-group-editor-permissions-ordering + # Invoking `register` from `ready` confirms that it does not perform any database queries - + # if it did, it would fail (on a standard test run without --keepdb at least) because the + # test database hasn't been migrated yet. + from wagtail.users.permission_order import register + + register("snippetstests.fancysnippet", order=999) diff --git a/wagtail/users/permission_order.py b/wagtail/users/permission_order.py index 2314deff3f..fb4fac507b 100644 --- a/wagtail/users/permission_order.py +++ b/wagtail/users/permission_order.py @@ -2,6 +2,7 @@ from django.contrib.contenttypes.models import ContentType from wagtail.coreutils import resolve_model_string +content_types_to_register = [] CONTENT_TYPE_ORDER = {} @@ -13,5 +14,18 @@ def register(model, **kwargs): """ order = kwargs.pop("order", None) if order is not None: - content_type = ContentType.objects.get_for_model(resolve_model_string(model)) - CONTENT_TYPE_ORDER[content_type.id] = order + # We typically call this at application startup, when the database may not be ready, + # and so we can't look up the content type yet. Instead we will queue up the + # (model, order) pair to be processed when the lookup is requested. + content_types_to_register.append((model, order)) + + +def get_content_type_order_lookup(): + if content_types_to_register: + for model, order in content_types_to_register: + content_type = ContentType.objects.get_for_model( + resolve_model_string(model) + ) + CONTENT_TYPE_ORDER[content_type.id] = order + content_types_to_register.clear() + return CONTENT_TYPE_ORDER diff --git a/wagtail/users/templatetags/wagtailusers_tags.py b/wagtail/users/templatetags/wagtailusers_tags.py index 3c2ca0e2df..7d39f38221 100644 --- a/wagtail/users/templatetags/wagtailusers_tags.py +++ b/wagtail/users/templatetags/wagtailusers_tags.py @@ -11,7 +11,7 @@ from django.utils.translation import gettext_noop from wagtail import hooks from wagtail.admin.models import Admin from wagtail.coreutils import accepts_kwarg -from wagtail.users.permission_order import CONTENT_TYPE_ORDER +from wagtail.users.permission_order import get_content_type_order_lookup from wagtail.utils.deprecation import RemovedInWagtail70Warning register = template.Library() @@ -96,9 +96,10 @@ def format_permissions(permission_bound_field): # get a distinct and ordered list of the content types that these permissions relate to. # relies on Permission model default ordering, dict.fromkeys() retaining that order # from the queryset, and the stability of sorted(). + content_type_order = get_content_type_order_lookup() content_type_ids = sorted( dict.fromkeys(permissions.values_list("content_type_id", flat=True)), - key=lambda ct: CONTENT_TYPE_ORDER.get(ct, float("inf")), + key=lambda ct: content_type_order.get(ct, float("inf")), ) # iterate over permission_bound_field to build a lookup of individual renderable