Some times surfacing multiple validation issues with the data is necessary. For example in this case (implementation of pairwise is omitted):
from marshmallow import Schema, fields, validates_schema, ValidationError
class NumberSchema(Schema):
number = fields.Integer()
class ListSchema(Schema):
data = fields.Nested(NumberSchema, many=True)
@validates_schema(pass_many=True)
def validate_numbers(self, data, many):
if not many:
return
for i, (n1, n2) in enumerate(pairwise(data)):
if n1 >= n2:
raise ValidationError('this number must be smaller than the previous one', '{}.number'.format(i+1))
if the list of numbers has multiple issues, only the first encountered issue is reported. This is sometimes not ideal for user interfaces (the user would appreciate all issues displayed at once).
I would suggest to allow validators return a list of ValidationError to indicate multiple issues.
Looks like the ValidationError can contain multiple validation errors in a list or dictionary format. That's probably the right way to return multiple issues with the data.
This should be better documented though, I had to read some source code to figure out.
@zhaostu This is a long struggle between me and library authors (See this issue and this pullrequest).
At my current work we needed support for this advanced validation, so I did table flip and now we use my fork of Marshmallow with all features WE need.
Also, I decided to start my own validation library - lollipop - pretty much similar to marshmallow but IMO does some things better. It still misses some planned features but already meets our needs, so I recommend giving it a try.
@maximkulkin I figured the schema validation doesn't support raising different errors or nested fields. And my previous comments were wrong, I couldn't get it to work the way I wanted.. :disappointed:
@zhaostu Yes. Your options are either to fork marshmallow and apply patch I mentioned or try another library.
After reading this discussion, I set out to try solve this myself. I implemented a POC function
def merge_schema_errors(errors):
if errors and errors['_schema']:
schema_errors = errors['_schema']
del errors['_schema']
for f in schema_errors:
for field, field_errs in f.items():
if field not in errors:
errors[field] = []
errors[field].extend(field_errs)
return errors
Which did what I wanted it to, provided I called ValidationError like this:
raise ValidationError({'fieldName': ['Error message here']})
This has a terrible interface, but my use case required it..
But, whilst finishing off writing my validation errors, I noticed the docstring for ValidationError specifies that you can actually provide a tuple or list of field of where to apply the message specified as the first argument of ValidationError
So without any extra code, I got this working by using
ValidationError('This is my validation error', field_names=['myField', 'myField2'])
I hope this clears things up for anyone looking here in the future.
Most helpful comment
After reading this discussion, I set out to try solve this myself. I implemented a POC function
Which did what I wanted it to, provided I called
ValidationErrorlike this:This has a terrible interface, but my use case required it..
But, whilst finishing off writing my validation errors, I noticed the docstring for
ValidationErrorspecifies that you can actually provide a tuple or list of field of where to apply the message specified as the first argument ofValidationErrorSo without any extra code, I got this working by using
I hope this clears things up for anyone looking here in the future.