Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested validation with the flask-restful RequestParser

Using the flask-restful micro-framework, I am having trouble constructing a RequestParser that will validate nested resources. Assuming an expected JSON resource format of the form:

{     'a_list': [         {             'obj1': 1,             'obj2': 2,             'obj3': 3         },         {             'obj1': 1,             'obj2': 2,             'obj3': 3         }     ] } 

Each item in a_list corresponds to an object:

class MyObject(object):     def __init__(self, obj1, obj2, obj3)         self.obj1 = obj1         self.obj2 = obj2         self.obj3 = obj3 

... and one would then create a RequestParser using a form something like:

from flask.ext.restful import reqparse parser = reqparse.RequestParser() parser.add_argument('a_list', type=MyObject, action='append') 

... but how would you validate the nested MyObjects of each dictionary inside a_list? Or, alternately, is this the wrong approach?

The API this corresponds to treats each MyObject as, essentially, an object literal, and there may be one or more of them passed to the service; therefore, flattening the resource format will not work for this circumstance.

like image 519
Daniel Naab Avatar asked Oct 07 '13 21:10

Daniel Naab


2 Answers

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) 
like image 143
barqshasbite Avatar answered Sep 19 '22 20:09

barqshasbite


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 Validator import json   def 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') 
like image 38
kardaj Avatar answered Sep 21 '22 20:09

kardaj