Allow GroupViewSet to be customised (#6477)

pull/7178/head
Jan Seifert 2020-10-20 16:01:30 +02:00 zatwierdzone przez Matt Westcott
rodzic 3f128b554e
commit 9dda314263
7 zmienionych plików z 186 dodań i 4 usunięć

Wyświetl plik

@ -0,0 +1,111 @@
Custom group edit/create page
=============================
Custom group edit/create page example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This example shows how to customize group 'edit' and 'create' page in Wagtail
admin.
Let's say you need to connect Active Directory groups with Django groups.
So create a model for Active Directory groups.
.. code-block:: python
from django.contrib.auth.models import Group
from django.db import models
class ADGroup(models.Model):
guid = models.CharField(verbose_name="GUID", max_length=64, db_index=True, unique=True)
name = models.CharField(verbose_name="Group", max_length=255)
domain = models.CharField(verbose_name="Domain", max_length=255, db_index=True)
description = models.TextField(verbose_name="Description", blank=True, null=True)
roles = models.ManyToManyField(Group, verbose_name="Role", related_name="adgroups", blank=True)
class Meta:
verbose_name = "AD group"
verbose_name_plural = "AD groups"
However, there is no role field on the Wagtail group 'edit' or 'create' page.
To add it, inherit from Wagtail group form and add a new field.
.. code-block:: python
from django import forms
from wagtail.users.forms import GroupForm as WagtailGroupForm
from .models import ADGroup
class GroupForm(WagtailGroupForm):
adgroups = forms.ModelMultipleChoiceField(
label="AD groups",
required=False,
queryset=ADGroup.objects.order_by("name"),
)
class Meta(WagtailGroupForm.Meta):
fields = WagtailGroupForm.Meta.fields + ("adgroups",)
def __init__(self, initial=None, instance=None, **kwargs):
if instance is not None:
if initial is None:
initial = {}
initial["adgroups"] = instance.adgroups.all()
super().__init__(initial=initial, instance=instance, **kwargs)
def save(self, commit=True):
instance = super().save()
instance.adgroups.set(self.cleaned_data["adgroups"])
return instance
Now add your custom form into group viewset by inheriting default Wagtail
``GroupViewSet`` class and overriding ``get_form_class`` method.
.. code-block:: python
from wagtail.users.views.groups import GroupViewSet as WagtailGroupViewSet
from .forms import GroupForm
class GroupViewSet(WagtailGroupViewSet):
def get_form_class(self, for_update=False):
return GroupForm
Append the field into group 'edit'/'create' templates.
.. code-block:: html+Django
{% extends "wagtailusers/groups/edit.html" %}
{% load wagtailusers_tags wagtailadmin_tags i18n %}
{% block extra_fields %}
{% include "wagtailadmin/shared/field_as_li.html" with field=form.adgroups %}
{% endblock extra_fields %}
Finally configure ``wagtail.users`` application for using the viewset. Create
``myapplication/apps.py`` module in the main application package and configure
``AppConfig``.
.. code-block:: python
from wagtail.users.apps import WagtailUsersAppConfig
class CustomUsersAppConfig(WagtailUsersAppConfig):
group_viewset = "myapplication.someapp.viewsets.GroupViewSet"
And put path to ``CustomUsersAppConfig`` into ``settings.INSTALLED_APPS``
instead of ``wagtail.users``.
.. code-block:: python
INSTALLED_APPS = [
...,
"myapplication.apps.CustomUsersAppConfig",
# "wagtail.users",
...,
]

Wyświetl plik

@ -11,5 +11,6 @@ Customising Wagtail
extending_hallo
admin_templates
custom_user_models
custom_group_viewset
streamfield_blocks
custom_account_settings

Wyświetl plik

@ -7,3 +7,4 @@ class WagtailUsersAppConfig(AppConfig):
label = 'wagtailusers'
verbose_name = _("Wagtail users")
default_auto_field = 'django.db.models.AutoField'
group_viewset = None

Wyświetl plik

@ -20,7 +20,10 @@
{% csrf_token %}
<ul class="fields">
{% include "wagtailadmin/shared/field_as_li.html" with field=form.name %}
{% block fields %}
{% include "wagtailadmin/shared/field_as_li.html" with field=form.name %}
{% block extra_fields %}{% endblock extra_fields %}
{% endblock fields %}
<li>
{% format_permissions permission_bound_field=form.permissions %}
</li>

Wyświetl plik

@ -22,7 +22,10 @@
{% csrf_token %}
<ul class="fields">
{% include "wagtailadmin/shared/field_as_li.html" with field=form.name %}
{% block fields %}
{% include "wagtailadmin/shared/field_as_li.html" with field=form.name %}
{% block extra_fields %}{% endblock extra_fields %}
{% endblock fields %}
<li>
{% format_permissions permission_bound_field=form.permissions %}

Wyświetl plik

@ -1,6 +1,7 @@
import unittest
import unittest.mock
from django import forms
from django.apps import apps
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
@ -16,7 +17,9 @@ from wagtail.core.models import Collection, GroupCollectionPermission, GroupPage
from wagtail.tests.utils import WagtailTestUtils
from wagtail.users.forms import UserCreationForm, UserEditForm
from wagtail.users.models import UserProfile
from wagtail.users.views.groups import GroupViewSet
from wagtail.users.views.users import get_user_creation_form, get_user_edit_form
from wagtail.users.wagtail_hooks import get_group_viewset_cls
delete_user_perm_codename = "delete_{0}".format(AUTH_USER_MODEL_NAME.lower())
@ -37,6 +40,10 @@ class CustomUserEditForm(UserEditForm):
attachment = forms.FileField(required=True, label="Attachment")
class CustomGroupViewSet(GroupViewSet):
icon = 'custom-icon'
class TestUserFormHelpers(TestCase):
def test_get_user_edit_form_with_default_form(self):
@ -1576,3 +1583,33 @@ class TestGroupEditView(TestCase, WagtailTestUtils):
# See that the non-registered permission is still there
self.assertEqual(self.test_group.permissions.count(), 1)
self.assertEqual(self.test_group.permissions.all()[0], self.non_registered_perm)
class TestGroupViewSet(TestCase):
def setUp(self):
self.app_config = apps.get_app_config('wagtailusers')
def test_get_group_viewset_cls(self):
self.assertIs(get_group_viewset_cls(self.app_config), GroupViewSet)
def test_get_group_viewset_cls_with_custom_form(self):
with unittest.mock.patch.object(
self.app_config, 'group_viewset', new='wagtail.users.tests.CustomGroupViewSet'
):
group_viewset = get_group_viewset_cls(self.app_config)
self.assertIs(group_viewset, CustomGroupViewSet)
self.assertEqual(group_viewset.icon, 'custom-icon')
def test_get_group_viewset_cls_custom_form_invalid_value(self):
with unittest.mock.patch.object(self.app_config, 'group_viewset', new=12345):
with self.assertRaises(ImproperlyConfigured) as exc_info:
get_group_viewset_cls(self.app_config)
self.assertTrue('refers to a class that is not class path' in str(exc_info.exception))
def test_get_group_viewset_cls_custom_form_does_not_exist(self):
with unittest.mock.patch.object(
self.app_config, 'group_viewset', new='wagtail.users.tests.CustomClassDoesNotExist'
):
with self.assertRaises(ImproperlyConfigured) as exc_info:
get_group_viewset_cls(self.app_config)
self.assertTrue('refers to a class that is not available' in str(exc_info.exception))

Wyświetl plik

@ -1,6 +1,9 @@
from django.apps import apps
from django.contrib.auth.models import Permission
from django.core.exceptions import ImproperlyConfigured
from django.db.models import Q
from django.urls import include, path, reverse
from django.utils.module_loading import import_string
from django.utils.translation import gettext_lazy as _
from wagtail.admin.menu import MenuItem
@ -20,9 +23,32 @@ def register_admin_urls():
]
def get_group_viewset_cls(app_config):
if app_config.group_viewset is None:
group_viewset_cls = GroupViewSet
else:
if not isinstance(app_config.group_viewset, str):
raise ImproperlyConfigured(
"'{:s}.group_viewset' refers to a class that is not class path".format(
app_config.__class__.__name__
)
)
try:
group_viewset_cls = import_string(app_config.group_viewset)
except ImportError:
raise ImproperlyConfigured(
"'{:s}.group_viewset' refers to a class that is not available".format(
app_config.__class__.__name__
)
)
return group_viewset_cls
@hooks.register('register_admin_viewset')
def register_viewset():
return GroupViewSet('wagtailusers_groups', url_prefix='groups')
app_config = apps.get_app_config("wagtailusers")
group_viewset_cls = get_group_viewset_cls(app_config)
return group_viewset_cls('wagtailusers_groups', url_prefix='groups')
# Typically we would check the permission 'auth.change_user' (and 'auth.add_user' /