From 397edf2ec50fec3603de05d563842f4ff2b1b7f5 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Mon, 10 Mar 2025 20:55:50 +0000 Subject: [PATCH] Support deferring validation on StreamField --- wagtail/admin/panels/field_panel.py | 3 +- wagtail/admin/tests/pages/test_create_page.py | 63 +++++++++++++++++++ wagtail/blocks/base.py | 3 + 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/wagtail/admin/panels/field_panel.py b/wagtail/admin/panels/field_panel.py index 02f07b2fcf..3ad200f5f4 100644 --- a/wagtail/admin/panels/field_panel.py +++ b/wagtail/admin/panels/field_panel.py @@ -69,7 +69,8 @@ class FieldPanel(Panel): else: required_on_save = getattr(db_field, "required_on_save", False) or ( db_field.null is False - and db_field.get_internal_type() not in ("CharField", "TextField") + and db_field.get_internal_type() + not in ("CharField", "TextField", "JSONField") ) if not required_on_save: diff --git a/wagtail/admin/tests/pages/test_create_page.py b/wagtail/admin/tests/pages/test_create_page.py index 3aea7541fd..0af5e5583f 100644 --- a/wagtail/admin/tests/pages/test_create_page.py +++ b/wagtail/admin/tests/pages/test_create_page.py @@ -32,6 +32,7 @@ from wagtail.test.testapp.models import ( StandardIndex, ) from wagtail.test.utils import WagtailTestUtils +from wagtail.test.utils.form_data import nested_form_data, streamfield from wagtail.test.utils.timestamps import submittable_timestamp @@ -755,6 +756,68 @@ class TestPageCreation(WagtailTestUtils, TestCase): ).exists() ) + def test_create_streampage_post_with_blank_body(self): + post_data = nested_form_data( + { + "title": "Stream page", + "slug": "stream-page", + "body": streamfield([]), + } + ) + response = self.client.post( + reverse( + "wagtailadmin_pages:add", + args=("tests", "streampage", self.root_page.id), + ), + post_data, + ) + # Find the page and check it + page = Page.objects.get( + path__startswith=self.root_page.path, slug="stream-page" + ).specific + + # Should be redirected to edit page + self.assertRedirects( + response, reverse("wagtailadmin_pages:edit", args=(page.id,)) + ) + + self.assertEqual(len(page.body), 0) + self.assertFalse(page.live) + + def test_cannot_publish_streampage_with_blank_body(self): + post_data = nested_form_data( + { + "title": "Stream page", + "slug": "stream-page", + "body": streamfield([]), + "action-publish": "Publish", + } + ) + response = self.client.post( + reverse( + "wagtailadmin_pages:add", + args=("tests", "streampage", self.root_page.id), + ), + post_data, + ) + self.assertEqual(response.status_code, 200) + + # Check that a form error was raised. The actual message as rendered + # ("This field is required.") is passed to the StreamBlock as part of + # StreamBlockValidationError.non_block_errors, whereas assertFormError + # only considers the message attribute (which is a generic error). + self.assertFormError( + response.context["form"], "body", "Validation error in StreamBlock" + ) + self.assertContains(response, "This field is required.") + + # Page should not have been created + self.assertFalse( + Page.objects.filter( + path__startswith=self.root_page.path, slug="stream-page" + ).exists() + ) + def test_create_simplepage_scheduled(self): go_live_at = timezone.now() + datetime.timedelta(days=1) expire_at = timezone.now() + datetime.timedelta(days=2) diff --git a/wagtail/blocks/base.py b/wagtail/blocks/base.py index 53138f64c5..5c4ce2f2c3 100644 --- a/wagtail/blocks/base.py +++ b/wagtail/blocks/base.py @@ -756,6 +756,9 @@ class BlockField(forms.Field): super().__init__(**kwargs) def clean(self, value): + # Pass required flag to the top-level block, so that dynamically setting it on the + # field (e.g. by defer_required_fields) is respected by the block. + self.block.set_meta_options({"required": self.required}) return self.block.clean(value) def has_changed(self, initial_value, data_value):