Validation errors applied to ListBlocks inside StructBlocks don't get rendered.
See original GitHub issueI’ve got a custom clean()
method on my StructBlock subclass, TestBlock
. TestBlock
contains two child blocks: text_field
(a CharBlock) and list_field
(a ListBlock of CharBlocks). My custom clean()
method ensures that the user provides at least three elements to list_field
, otherwise it throws a ValidationError
. Here’s the code:
class TestBlock(blocks.StructBlock):
text_field = blocks.CharBlock(
label='Text',
max_length=255
)
list_field = blocks.ListBlock(
blocks.CharBlock(max_length=255),
default=[],
)
class Meta:
label = 'Test Block'
template = 'app/blocks/TestBlock.html'
form_classname = 'test-block struct-block'
def clean(self, value):
result = super().clean(value)
errors = {}
list_field_count = len(value['list_field'])
if list_field_count < 3:
errors['list_field'] = ErrorList(['You must provide at least three.'])
if errors:
# The message here is arbitrary - StructBlock.render_form will suppress it
# and delegate the errors contained in the 'params' dict to the child blocks
raise ValidationError('TableBlock received invalid data', params=errors)
return result
The problem is that while the validator will successfully mark the form with an error if the user provides less than 3 elements for list_field
, the form will not render the “You must provide at least three” message anywhere. Here’s a screenshot of what I mean:
However, by changing errors['list_field']
to errors['text_field']
in the clean()
method, you get this:
So the error rendering is functioning properly on CharBlock. It’s just not working on ListBlock, for some reason.
Issue Analytics
- State:
- Created 4 years ago
- Comments:6 (3 by maintainers)
Top GitHub Comments
@loicteixeira I’m running into this same problem, and I do think this is a bug.
With the explanation that was given, I don’t see how it’s not a bug. The code snippet you provided is the
clean
method for the ListBlock, but the original problem is the override of theclean
method on the StructBlock specifically, wherein overriding this should apply the appropriate error to the list block itself, and not “Within” the list block.Further, even if the ListBlock has a different definition for Clean, then it should behave the same as other fields within a StructBlock no? i.e. the clean definition on ListBlock should not interfere with the StructBlock’s clean method.
I agree with @loicteixeira’s assessment. Validation error handling on StreamField blocks works on the contract that whenever a block’s
clean
method throws an exception, that same exception object will ultimately be passed back to that block’srender_form
method to be rendered back to the user.Since the top-level StreamField’s
clean
method can only throw one exception back to Django’s forms framework - even if there are multiple errors within it - any block that acts as a container for child blocks (such as StructBlock and ListBlock) is responsible for bundling up the exceptions thrown by its children into a singleValidationError
, and then unbundling them again inrender_form
to pass each of those exceptions back to the relevant child’srender_form
method.It’s important to note that this “bundling” mechanism is private to each container block type, and any
ValidationError
objects emerging from it should be treated as a “black box” - they are liable to contain a specially-craftedparams
dict that is only understood by that one block.The line
errors['list_field'] = ErrorList(['You must provide at least three.'])
is breaking this contract - it’s constructing aValidationError
that will ultimately be pushed toListBlock.render_form
but doesn’t followListBlock
’s internal format forValidationError
s.The piece that’s really missing here is that
ListBlock
currently has no concept of “non field errors” - it assumes that any validation errors are the responsibility of one of its child blocks. Once you implement that, then you’re 90% of the way towards supportingmin_num
/max_num
arguments onListBlock
, which I suspect is the feature you’re really looking for here 😃 And, indeed, one of the things #3914 implements is a ValidationError subclass with provision for non-block errors, to match the one that already exists for StreamBlock.(I’m half inclined to close this as a duplicate of #2379 - but on the other hand #3914 is largely blocked by the Javascript code, which would be part of a complete
min_num
/max_num
solution but out of the scope of what’s being discussed here - so I think we can consider the server-side validation to be a useful stepping stone. In other words: adding server-side validation formin_num
/max_num
on ListBlock will close this ticket, and adding the corresponding constraints on the client-side UI will close #2379.)