diff --git a/wagtail/blocks/base.py b/wagtail/blocks/base.py index 5c4ce2f2c3..cd73c8098e 100644 --- a/wagtail/blocks/base.py +++ b/wagtail/blocks/base.py @@ -756,10 +756,22 @@ 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) + from wagtail.blocks.stream_block import StreamBlock + + if isinstance(self.block, StreamBlock): + # StreamBlock is the only block type that is formally-supported as the top level block + # of a BlockField, but it's possible that other block types could be used, so check + # this explicitly. + # self.block has a `required` attribute that is consistent with the StreamField's `blank` + # attribute and thus the `required` attribute of BlockField - but if the latter has been + # assigned dynamically (e.g. by defer_required_fields) we want this to take precedence. + # We do this through the `ignore_required_constraints` flag recognised by + # StreamBlock.clean. + return self.block.clean( + value, ignore_required_constraints=not self.required + ) + else: + return self.block.clean(value) def has_changed(self, initial_value, data_value): return self.block.get_prep_value(initial_value) != self.block.get_prep_value( diff --git a/wagtail/blocks/stream_block.py b/wagtail/blocks/stream_block.py index 95991ff28a..343754b76d 100644 --- a/wagtail/blocks/stream_block.py +++ b/wagtail/blocks/stream_block.py @@ -160,7 +160,7 @@ class BaseStreamBlock(Block): def required(self): return self.meta.required - def clean(self, value): + def clean(self, value, ignore_required_constraints=False): cleaned_data = [] errors = {} non_block_errors = ErrorList() @@ -179,7 +179,7 @@ class BaseStreamBlock(Block): % {"min_num": self.meta.min_num} ) ) - elif self.required and len(value) == 0: + elif self.required and not ignore_required_constraints and len(value) == 0: non_block_errors.append(ValidationError(_("This field is required."))) if self.meta.max_num is not None and self.meta.max_num < len(value):