Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple request parsing without reqparse.RequestParser()

flask_restful.reqparse has been deprecated (https://flask-restful.readthedocs.io/en/latest/reqparse.html):

The whole request parser part of Flask-RESTful is slated for removal and will be replaced by documentation on how to integrate with other packages that do the input/output stuff better (such as marshmallow). This means that it will be maintained until 2.0 but consider it deprecated. Don’t worry, if you have code using that now and wish to continue doing so, it’s not going to go away any time too soon.

I've looked briefly at Marshmallow and still a bit confused about how to use it if I wanted to replace reqparse.RequestParser(). What would we write instead of something like the following:

from flask import Flask, request, Response
from flask_restful import reqparse

@app.route('/', methods=['GET'])
def my_api() -> Response:
   parser = reqparse.RequestParser()
   parser.add_argument('username', type=str, required=True)
   args = parser.parse_args()
   return {'message': 'cool'}, 200

(after half an hour of reading some more documentation…)

RequestParser looks at the MultiDict request.values by default (apparently query parameters, then form body parameters according to https://stackoverflow.com/a/16664376/5139284). So then we just need to validate the data in request.values somehow.

Here's a snippet of some relevant code from Marshmallow. It seems a good deal more involved than reqparse: first you create a schema class, then instantiate it, then have it load the request JSON. I'd rather not have to write a separate class for each API endpoint. Is there something more lightweight similar to reqparse, where you can write all the types of the argument validation information within the function defining your endpoint?

from flask import Flask, request, Response
from flask_restful import reqparse
from marshmallow import (
    Schema,
    fields,
    validate,
    pre_load,
    post_dump,
    post_load,
    ValidationError,
)

class UserSchema(Schema):
    id = fields.Int(dump_only=True)
    email = fields.Str(
        required=True, validate=validate.Email(error="Not a valid email address")
    )
    password = fields.Str(
        required=True, validate=[validate.Length(min=6, max=36)], load_only=True
    )
    joined_on = fields.DateTime(dump_only=True)

user_schema = UserSchema()

@app.route("/register", methods=["POST"])
def register():
    json_input = request.get_json()
    try:
        data = user_schema.load(json_input)
    except ValidationError as err:
        return {"errors": err.messages}, 422
    # etc.
like image 520
mic Avatar asked Nov 07 '22 08:11

mic


1 Answers

If your endpoints share any commonalities in schema, you can use fields.Nested() to nest definitions within each Marshmallow class, which may save on code writing for each endpoint. Docs are here.

For example, for operations that update a resource called 'User', you would likely need a standardised subset of user information to conduct the operation, such as user_id, user_login_status, user_authorisation_level etc. These can be created once and nested in new classes for more specific user operations, for example updating a user's account:

class UserData(Schema):
    user_id = fields.Int(required=True)
    user_login_status = fields.Boolean(required=True)
    user_authentication_level = fields.Int(required=True)
    # etc ....

class UserAccountUpdate(Schema):
    created_date = fields.DateTime(required=True)
    user_data = fields.Nested(UserData)
    # account update fields...

like image 198
atomscale Avatar answered Nov 14 '22 21:11

atomscale