kopia lustrzana https://github.com/wagtail/wagtail
Allow GroupViewSet to be customised (#6477)
rodzic
3f128b554e
commit
9dda314263
|
@ -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",
|
||||||
|
...,
|
||||||
|
]
|
|
@ -11,5 +11,6 @@ Customising Wagtail
|
||||||
extending_hallo
|
extending_hallo
|
||||||
admin_templates
|
admin_templates
|
||||||
custom_user_models
|
custom_user_models
|
||||||
|
custom_group_viewset
|
||||||
streamfield_blocks
|
streamfield_blocks
|
||||||
custom_account_settings
|
custom_account_settings
|
||||||
|
|
|
@ -7,3 +7,4 @@ class WagtailUsersAppConfig(AppConfig):
|
||||||
label = 'wagtailusers'
|
label = 'wagtailusers'
|
||||||
verbose_name = _("Wagtail users")
|
verbose_name = _("Wagtail users")
|
||||||
default_auto_field = 'django.db.models.AutoField'
|
default_auto_field = 'django.db.models.AutoField'
|
||||||
|
group_viewset = None
|
||||||
|
|
|
@ -20,7 +20,10 @@
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
<ul class="fields">
|
<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>
|
<li>
|
||||||
{% format_permissions permission_bound_field=form.permissions %}
|
{% format_permissions permission_bound_field=form.permissions %}
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -22,7 +22,10 @@
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
<ul class="fields">
|
<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>
|
<li>
|
||||||
{% format_permissions permission_bound_field=form.permissions %}
|
{% format_permissions permission_bound_field=form.permissions %}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import unittest
|
import unittest.mock
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import Group, Permission
|
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.tests.utils import WagtailTestUtils
|
||||||
from wagtail.users.forms import UserCreationForm, UserEditForm
|
from wagtail.users.forms import UserCreationForm, UserEditForm
|
||||||
from wagtail.users.models import UserProfile
|
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.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())
|
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")
|
attachment = forms.FileField(required=True, label="Attachment")
|
||||||
|
|
||||||
|
|
||||||
|
class CustomGroupViewSet(GroupViewSet):
|
||||||
|
icon = 'custom-icon'
|
||||||
|
|
||||||
|
|
||||||
class TestUserFormHelpers(TestCase):
|
class TestUserFormHelpers(TestCase):
|
||||||
|
|
||||||
def test_get_user_edit_form_with_default_form(self):
|
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
|
# See that the non-registered permission is still there
|
||||||
self.assertEqual(self.test_group.permissions.count(), 1)
|
self.assertEqual(self.test_group.permissions.count(), 1)
|
||||||
self.assertEqual(self.test_group.permissions.all()[0], self.non_registered_perm)
|
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))
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
from django.apps import apps
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.urls import include, path, reverse
|
from django.urls import include, path, reverse
|
||||||
|
from django.utils.module_loading import import_string
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from wagtail.admin.menu import MenuItem
|
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')
|
@hooks.register('register_admin_viewset')
|
||||||
def register_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' /
|
# Typically we would check the permission 'auth.change_user' (and 'auth.add_user' /
|
||||||
|
|
Ładowanie…
Reference in New Issue