Nested validation with the flask-restful RequestParser Nested validation with the flask-restful RequestParser flask flask

Nested validation with the flask-restful RequestParser


I have had success by creating RequestParser instances for the nested objects. Parse the root object first as you normally would, then use the results to feed into the parsers for the nested objects.

The trick is the location argument of the add_argument method and the req argument of the parse_args method. They let you manipulate what the RequestParser looks at.

Here's an example:

root_parser = reqparse.RequestParser()root_parser.add_argument('id', type=int)root_parser.add_argument('name', type=str)root_parser.add_argument('nested_one', type=dict)root_parser.add_argument('nested_two', type=dict)root_args = root_parser.parse_args()nested_one_parser = reqparse.RequestParser()nested_one_parser.add_argument('id', type=int, location=('nested_one',))nested_one_args = nested_one_parser.parse_args(req=root_args)nested_two_parser = reqparse.RequestParser()nested_two_parser.add_argument('id', type=int, location=('nested_two',))nested_two_args = nested_two_parser.parse_args(req=root_args)


I would suggest using a data validation tool such as cerberus. You start by defining a validation schema for your object (Nested object schema is covered in this paragraph), then use a validator to validate the resource against the schema. You also get detailed error messages when the validation fails.

In the following example, I want to validate a list of locations:

from cerberus import Validatorimport jsondef location_validator(value):    LOCATION_SCHEMA = {        'lat': {'required': True, 'type': 'float'},        'lng': {'required': True, 'type': 'float'}    }    v = Validator(LOCATION_SCHEMA)    if v.validate(value):        return value    else:        raise ValueError(json.dumps(v.errors))

The argument is defined as follows:

parser.add_argument('location', type=location_validator, action='append')


Since the type argument here is nothing but a callable that either returns a parsed value or raise ValueError on invalid type, I would suggest creating your own type validator for this. The validator could look something like:

from flask.ext.restful import reqparsedef myobj(value):    try:        x = MyObj(**value)    except TypeError:        # Raise a ValueError, and maybe give it a good error string        raise ValueError("Invalid object")    except:        # Just in case you get more errors        raise ValueError     return x#and now inside your views...parser = reqparse.RequestParser()parser.add_argument('a_list', type=myobj, action='append')