Fix - Ensure `WagtailAdminFormPageForm.clean` returns `cleaned_data`

Return `cleaned_data` for more consistent subclassing.

Updated documentation with an example of adding custom page validation for form fields.

Add unit test to ensure that the documented usage of extending `WagtailAdminFormPageForm` works as expected.

Originally from #10375
pull/12536/head
John-Scott Atlakson 2023-04-20 11:36:53 -07:00 zatwierdzone przez Matt Westcott
rodzic f600a356e6
commit 9d31fd3fa5
4 zmienionych plików z 89 dodań i 9 usunięć

Wyświetl plik

@ -825,3 +825,32 @@ class EmailFormPage(EmailFormMixin, FormMixin, BasePage):
# ...
```
(form_builder_custom_admin_validation)=
## Custom validation for admin form pages
By default, pages that inherit from `FormMixin` will validate that each field added by an editor has a unique `clean_name`.
If you need to add custom validation, create a subclass of `WagtailAdminFormPageForm` and add your own `clean` definition and set the `base_form_class` on your `Page` model.
```{note}
Validation only applies when editors use the form builder to add fields in the Wagtail admin,
not when the form is submitted by end users.
```
```python
from wagtail.models import Page
from wagtail.contrib.forms.models import FormMixin, WagtailAdminFormPageForm
class CustomWagtailAdminFormPageForm(WagtailAdminFormPageForm):
def clean(self):
cleaned_data = super().clean()
# Insert custom validation here, see `WagtailAdminFormPageForm.clean` for an example
return cleaned_data
class FormPage(AbstractForm):
base_form_class = CustomWagtailAdminFormPageForm
```

Wyświetl plik

@ -173,11 +173,12 @@ class SelectDateForm(django.forms.Form):
class WagtailAdminFormPageForm(WagtailAdminPageForm):
def clean(self):
super().clean()
cleaned_data = super().clean()
related_name = "form_fields"
# Check for duplicate form fields by comparing their internal clean_names
if "form_fields" in self.formsets:
forms = self.formsets["form_fields"].forms
forms = self.formsets[related_name].forms
for form in forms:
form.is_valid()
@ -193,7 +194,7 @@ class WagtailAdminFormPageForm(WagtailAdminPageForm):
if duplicate_clean_name:
duplicate_form_field = next(
f
for f in self.formsets["form_fields"].forms
for f in self.formsets[related_name].forms
if f.instance.get_field_clean_name() == duplicate_clean_name
)
duplicate_form_field.add_error(
@ -205,3 +206,5 @@ class WagtailAdminFormPageForm(WagtailAdminPageForm):
% {"label_name": duplicate_form_field.instance.label}
),
)
return cleaned_data

Wyświetl plik

@ -2085,6 +2085,35 @@ class TestFormPageCreate(WagtailTestUtils, TestCase):
response, reverse("wagtailadmin_pages:edit", args=(page.id,))
)
def test_form_page_creation_error_with_custom_clean_method(self):
"""
CustomFormPageSubmission uses a custom `base_form_class` with a clean method
that will raise a ValidationError if the from email contains 'example.com'.
"""
post_data = {
"title": "Drink selection",
"slug": "drink-selection",
"from_address": "bad@example.com",
}
response = self.client.post(
reverse(
"wagtailadmin_pages:add",
args=("tests", "formpagewithcustomsubmission", self.root_page.id),
),
post_data,
)
self.assertEqual(response.status_code, 200)
self.assertContains(
response, "The page could not be created due to validation errors"
)
self.assertContains(
response, "<li>Email cannot be from example.com</li>", count=1
)
class TestFormPageEdit(WagtailTestUtils, TestCase):
def setUp(self):

Wyświetl plik

@ -44,7 +44,7 @@ from wagtail.blocks import (
StreamBlock,
StructBlock,
)
from wagtail.contrib.forms.forms import FormBuilder
from wagtail.contrib.forms.forms import FormBuilder, WagtailAdminFormPageForm
from wagtail.contrib.forms.models import (
FORM_FIELD_CHOICES,
AbstractEmailForm,
@ -715,18 +715,37 @@ class FormPageWithRedirect(AbstractEmailForm):
# FormPage with a custom FormSubmission
class FormPageWithCustomSubmissionForm(WagtailAdminFormPageForm):
"""
Used to validate that admin forms can validate the page's submissions via
extending the form class.
"""
def clean(self):
cleaned_data = super().clean()
from_address = cleaned_data.get("from_address")
if from_address and "example.com" in from_address:
raise ValidationError("Email cannot be from example.com")
return cleaned_data
class FormPageWithCustomSubmission(AbstractEmailForm):
"""
This Form page:
* Have custom submission model
* Have custom related_name (see `FormFieldWithCustomSubmission.page`)
* Saves reference to a user
* Doesn't render html form, if submission for current user is present
A ``FormPage`` with a custom FormSubmission and other extensive customizations:
* A custom submission model
* A custom related_name (see `FormFieldWithCustomSubmission.page`)
* Saves reference to a user
* Doesn't render html form, if submission for current user is present
* A custom clean method that does not allow the ``from_address`` to be set to anything including example.com
"""
intro = RichTextField(blank=True)
thank_you_text = RichTextField(blank=True)
base_form_class = FormPageWithCustomSubmissionForm
def get_context(self, request, *args, **kwargs):
context = super().get_context(request)
context["greeting"] = "hello world"