Arguments

To inject arguments into a view function, use the Blueprint.arguments decorator. It allows to specify a Schema to deserialize and validate the parameters.

When processing a request, the input data is deserialized, validated, and injected in the view function.

@blp.route('/')
class Pets(MethodView):

    @blp.arguments(PetQueryArgsSchema, location='query')
    @blp.response(200, PetSchema(many=True))
    def get(self, args):
        return Pet.get(filters=args)

    @blp.arguments(PetSchema)
    @blp.response(201, PetSchema)
    def post(self, pet_data):
        return Pet.create(**pet_data)

Arguments Location

The following locations are allowed:

  • "json"
  • "query" (or "querystring")
  • "path"
  • "form"
  • "headers"
  • "cookies"
  • "files"

The location defaults to "json", which means body parameter.

Arguments Injection

By default, arguments are passed as a single positional dict argument. If as_kwargs=True is passed, the decorator passes deserialized input data as keyword arguments instead.

@blp.route('/')
class Pets(MethodView):

    @blp.arguments(PetQueryArgsSchema, location='query', as_kwargs=True)
    @blp.response(200, PetSchema(many=True))
    def get(self, **kwargs):
        return Pet.get(filters=**kwargs)

This decorator can be called several times on a resource function, for instance to accept both body and query parameters. The order of the decorator calls matters as it determines the order in which the parameters are passed to the view function.

@blp.route('/')
class Pets(MethodView):

    @blp.arguments(PetSchema)
    @blp.arguments(QueryArgsSchema, location='query')
    def post(pet_data, query_args):
        return Pet.create(pet_data, **query_args)

Dealing With Unknown Arguments

When input data contains unknown fields, a marshmallow Schema may raise a ValidationError (default), exclude those fields, or include them without validation. This can be controlled by the unknown Meta attribute of the Schema, which can be set to RAISE, EXCLUDE or INCLUDE.

Webargs, used internally by the arguments decorator, sets this parameter depending on the location of the argument. EXCLUDE is used for all locations except json, form, json_or_form, or path, where RAISE is used.

This can be customized in the FlaskParser imported from webargs. The easiest way is to mutate DEFAULT_UNKNOWN_BY_LOCATION in the parser class:

import marshmallow as ma
from webargs.flaskparser import FlaskParser

FlaskParser.DEFAULT_UNKNOWN_BY_LOCATION["json"] = ma.EXCLUDE

It can also be achieved by subclassing the parser and setting ARGUMENTS_PARSER in a base Blueprint class:

import marshmallow as ma
from webargs.flaskparser import FlaskParser
from flask_smorest import Blueprint

class MyFlaskParser(FlaskParser):
    DEFAULT_UNKNOWN_BY_LOCATION = {
        "query": ma.RAISE,
        "json": ma.RAISE,
        # ...
    }

class MyBlueprint(Blueprint):
    ARGUMENTS_PARSER = MyFlaskParser()

This latter method is recommended if several parsers are instantiated with different unknown values, for instance to get a different behaviour in different Blueprint s.

Setting None as DEFAULT_UNKNOWN_BY_LOCATION instead of a location/value mapping disables the feature to fall back to the Schema’s unknown value and marshmallow default (RAISE).

Note

The pagination feature in flask-smorest uses its own FlaskParser instance to parse pagination parameters from query arguments. It is affected by mutations of FlaskParser.DEFAULT_UNKNOWN_BY_LOCATION setting a value other than EXCLUDE for query.

Note

More default about customizing unknown can be found in webargs documentation.

Note

The unknown value passed by webargs only applies to first level fields, not to nested fields. To set EXCLUDE for json locations using Schema``s with ``Nested fields, one must set EXCLUDE in the parser but also set unkown=EXCLUDE as Meta attribute in nested schemas. In practice, this is easily achieved using a base schema class. This should be improved in the future, as discussed in webargs issue #580.

Multiple Arguments Schemas

Calls to arguments decorator can be stacked, for instance to define arguments from multiple locations:

@blp.route('/')
class Pets(MethodView):

    @blp.arguments(PetSchema)
    @blp.arguments(QueryArgsSchema, location="query")
    @blp.response(201, PetSchema)
    def post(self, pet_data, query_args):
        pet = Pet.create(**pet_data)
        # Use query args
        ...
        return pet

It can also be done to define multiple arguments for the same location. Of course, this only makes sense for locations where unknown is set to EXCLUDE.

Content Type

When using body arguments, a default content type is assumed depending on the location. The location / content type mapping can be customized by modifying Blueprint.DEFAULT_LOCATION_CONTENT_TYPE_MAPPING.

DEFAULT_LOCATION_CONTENT_TYPE_MAPPING = {
    "json": "application/json",
    "form": "application/x-www-form-urlencoded",
    "files": "multipart/form-data",

It is also possible to override those defaults in a single resource by passing a string as content_type argument to Blueprint.arguments.

Note

The content type is only used for documentation purpose and has no impact on request parsing.

Note

Multipart requests with mixed types (file, form, etc.) are not supported. They can be achieved but the documentation is not correctly generated. arguments decorator can be called multiple times on the same view function but it should not be called with more that one request body location. This limitation is discussed in #46.

File Upload

File uploads as multipart/form-data are supported for both OpenAPI 3 and OpenAPI 2.

The arguments Schema should contain Upload fields. The files are injected in the view function as a dict of werkzeug FileStorage instances.

import os.path
from werkzeug.utils import secure_filename
from flask_smorest.fields import Upload

class MultipartFileSchema(ma.Schema):
    file_1 = Upload()

@blp.route('/', methods=['POST'])
@blp.arguments(MultipartFileSchema, location='files')
@blp.response(201)
def func(files):
    base_dir = '/path/to/storage/dir/'
    file_1 = files['file_1']
    file_1.save(os.path.join(base_dir, secure_filename(file_1.filename)))