Validation imbriquée avec le flacon-restful RequestParser
 à l'aide du  flacon-restful  micro-framework, j'ai de la difficulté à construire un RequestParser  qui permettra de valider les ressources imbriquées.  En supposant un format de ressource JSON prévu de la forme:  
{
    'a_list': [
        {
            'obj1': 1,
            'obj2': 2,
            'obj3': 3
        },
        {
            'obj1': 1,
            'obj2': 2,
            'obj3': 3
        }
    ]
}
  
    chaque élément de a_list correspond à un objet: 
class MyObject(object):
    def __init__(self, obj1, obj2, obj3)
        self.obj1 = obj1
        self.obj2 = obj2
        self.obj3 = obj3
  
  ... et on créerait alors un RequestParser en utilisant un formulaire du genre:
from flask.ext.restful import reqparse
parser = reqparse.RequestParser()
parser.add_argument('a_list', type=MyObject, action='append')
  
   ... mais comment valideriez-vous les  MyObject  de chaque dictionnaire à l'intérieur de  a_list  ?  Ou, alternativement, est-ce la bonne approche?  
  L'API, ce qui correspond à la traite chaque  MyObject  que, pour l'essentiel, un objet littéral, et il peut y avoir un ou plusieurs d'entre eux sont passés au service; par conséquent, l'aplatissement de la ressource, le format ne fonctionnera pas pour cette circonstance.  
5 réponses
 j'ai eu du succès en créant des instances RequestParser  pour les objets imbriqués. Analyser l'objet racine d'abord comme vous le feriez normalement, puis utiliser les résultats pour nourrir les analyseurs pour les objets imbriqués.   
 l'astuce est l'argument location de la méthode add_argument et l'argument req de la méthode parse_args . Ils vous laissent manipuler ce que le RequestParser  regarde.  
voici un exemple:
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)
  
    	                  puisque l'argument  type  ici n'est rien d'autre qu'une callable qui renvoie soit une valeur parsée soit une augmentation de valeur sur un type invalide, je suggérerais de créer votre propre validateur de type pour cela. Le validateur pourrait ressembler à quelque chose comme:  
from flask.ext.restful import reqparse
def 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')
  
    	                j'ai trouvé le bbenne10s answer vraiment utile, mais il n'a pas fonctionné pour moi comme tel.
  la façon dont je l'ai fait est probablement mal, mais cela fonctionne. Mon problème est que je ne comprends pas ce que  action='append'  fait comme ce qu'il semble faire est    wrap    la valeur reçue dans une liste, mais il n'a pas de sens pour moi. Quelqu'un peut-il expliquer ce que cela signifie dans les commentaires?  
  alors ce que je finalement faire est de créer mon propre  listtype , obtenir la liste à l'intérieur du  value  param et puis itérer à travers la liste de cette façon:  
from flask.ext.restful import reqparse
def myobjlist(value):
    result = []
    try:
        for v in value:
            x = MyObj(**v)
            result.append(x)
    except TypeError:
        raise ValueError("Invalid object")
    except:
        raise ValueError
    return result
#and now inside views...
parser = reqparse.RequestParser()
parser.add_argument('a_list', type=myobjlist)
  
  pas une solution vraiment élégante, mais au moins il fait le travail. J'espère que quelqu'un peut nous indiquer la bonne direction...
mise à Jour
  Comme    bbenne10 a dit dans les commentaires    , ce  action='append'  n'est ajouter tous les les arguments nommés de la même façon dans une liste, donc dans le cas de L'OP, il ne semble pas être très utile.  
 j'ai itéré au-dessus de ma solution parce que je n'ai pas aimé le fait que reqparse n'analysait pas/validait aucun des objets imbriqués donc je ce que j'ai fait est d'utiliser reqparse à l'intérieur du type d'objet personnalisé myobjlist .  
  tout D'abord, j'ai déclaré une nouvelle sous-classe de  Request , à passer comme la demande lors de l'analyse des objets imbriqués:  
class NestedRequest(Request):
    def __init__(self, json=None, req=request):
        super(NestedRequest, self).__init__(req.environ, False, req.shallow)
        self.nested_json = json
    @property
    def json(self):
        return self.nested_json
  
   cette classe remplace la request.json  de sorte qu'elle utilise un nouveau json ayant pour objet d'être interprété.
Ensuite, j'ai ajouté un parser  reqparse à myobjlist pour analyser tous les arguments et j'ai ajouté un sauf pour attraper l'erreur d'analyse et passer le message reqparse .  
from flask.ext.restful import reqparse
from werkzeug.exceptions import ClientDisconnected
def myobjlist(value):
    parser = reqparse.RequestParser()
    parser.add_argument('obj1', type=int, required=True, help='No obj1 provided', location='json')
    parser.add_argument('obj2', type=int, location='json')
    parser.add_argument('obj3', type=int, location='json')
    nested_request = NestedRequest()
    result = []
    try:
        for v in value:
            nested_request.nested_json = v
            v = parser.parse_args(nested_request)
            x = MyObj(**v)
            result.append(x)
    except TypeError:
        raise ValueError("Invalid object")
    except ClientDisconnected, e:
        raise ValueError(e.data.get('message', "Parsing error") if e.data else "Parsing error")
    except:
        raise ValueError
    return result
  
  de cette façon, même les objets imbriqués seront analysés par reqparse et montreront ses erreurs
je suggère d'utiliser un outil de validation des données tel que cerberus . Vous commencez par définir un schéma de validation pour votre objet (le schéma D'objet imbriqué est couvert dans ce "paragraphe ), puis utilisez un validateur pour valider la ressource par rapport au schéma. Vous recevez également des messages d'erreur détaillés lorsque la validation échoue.
dans l'exemple suivant, je veux valider une liste d'emplacements:
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))
  
  l'argument est défini comme suit:
parser.add_argument('location', type=location_validator, action='append')
  
    	                la solution la mieux notée ne supporte pas "strict=True", pour résoudre le problème "strict=True" ne supporte pas, vous pouvez créer un objet FakeRequest pour tricher RequestParser
class FakeRequest(dict):
    def __setattr__(self, name, value):
        object.__setattr__(self, name, value)
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=('json',))
fake_request = FakeRequest()
setattr(fake_request, 'json', root_args['nested_one'])
setattr(fake_request, 'unparsed_arguments', {})
nested_one_args = nested_one_parser.parse_args(req=fake_request, strict=True)
fake_request = FakeRequest()
setattr(fake_request, 'json', root_args['nested_two'])
setattr(fake_request, 'unparsed_arguments', {})
nested_two_parser = reqparse.RequestParser()
nested_two_parser.add_argument('id', type=int, location=('json',))
nested_two_args = nested_two_parser.parse_args(req=fake_request, strict=True)
  
  BTW: flask restful déchirera RequestParser dehors, et le remplacera par Marshmallow Lien