Rework deferring validation on StreamField

Overwriting a block's `required` option is not safe, as a block object is part of a class-level definition and is thus shared by all block instances, not just the one currently being validated. Instead, introduce a flag to Block.clean to skip 'required' validation.
pull/12946/head
Matt Westcott 2025-03-10 20:55:50 +00:00 zatwierdzone przez Matt Westcott
rodzic c38db51559
commit 6d01356ec6
2 zmienionych plików z 18 dodań i 6 usunięć

Wyświetl plik

@ -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(

Wyświetl plik

@ -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):