Advanced Usage

This section includes guides for advanced usage patterns.

Custom Location Handlers

To add your own custom location handler, write a function that receives a request, and a Schema, then decorate that function with Parser.location_loader.

from webargs import fields
from webargs.flaskparser import parser

def load_data(request, schema):

# Now 'data' can be specified as a location
@parser.use_args({"per_page": fields.Int()}, location="data")
def posts(args):
    return "displaying {} posts".format(args["per_page"])


The schema is passed so that it can be used to wrap multidict types and unpack List fields correctly. If you are writing a loader for a multidict type, consider looking at MultiDictProxy for an example of how to do this.

“meta” Locations

You can define your own locations which mix data from several existing locations.

The json_or_form location does this – first trying to load data as JSON and then falling back to a form body – and its implementation is quite simple:

def load_json_or_form(self, req, schema):
    """Load data from a request, accepting either JSON or form-encoded

    The data will first be loaded as JSON, and, if that fails, it will be
    loaded as a form post.
    data = self.load_json(req, schema)
    if data is not missing:
        return data
    return self.load_form(req, schema)

You can imagine your own locations with custom behaviors like this. For example, to mix query parameters and form body data, you might write the following:

from webargs import fields
from webargs.multidictproxy import MultiDictProxy
from webargs.flaskparser import parser

def load_data(request, schema):
    # relies on the Flask (werkzeug) MultiDict type's implementation of
    # these methods, but when you're extending webargs, you may know things
    # about your framework of choice
    newdata = request.args.copy()
    return MultiDictProxy(newdata, schema)

# Now 'query_and_form' means you can send these values in either location,
# and they will be *mixed* together into a new dict to pass to your schema
@parser.use_args({"favorite_food": fields.String()}, location="query_and_form")
def set_favorite_food(args):
    ...  # do stuff
    return "your favorite food is now set to {}".format(args["favorite_food"])

marshmallow Integration

When you need more flexibility in defining input schemas, you can pass a marshmallow Schema instead of a dictionary to Parser.parse, Parser.use_args, and Parser.use_kwargs.

from marshmallow import Schema, fields
from webargs.flaskparser import use_args

class UserSchema(Schema):
    id = fields.Int(dump_only=True)  # read-only (won't be parsed by webargs)
    username = fields.Str(required=True)
    password = fields.Str(load_only=True)  # write-only
    first_name = fields.Str(missing="")
    last_name = fields.Str(missing="")
    date_registered = fields.DateTime(dump_only=True)

    # NOTE: Uncomment below two lines if you're using marshmallow 2
    # class Meta:
    #    strict = True

def profile_view(args):
    username = args["userame"]
    # ...

def profile_update(username, password, first_name, last_name):
    update_profile(username, password, first_name, last_name)
    # ...

# You can add additional parameters
@use_kwargs({"posts_per_page": fields.Int(missing=10)}, location="query")
def profile_posts(args, posts_per_page):
    username = args["username"]
    # ...


If you’re using marshmallow 2, you should always set strict=True (either as a class Meta option or in the Schema’s constructor) when passing a schema to webargs. This will ensure that the parser’s error handler is invoked when expected.

When to avoid use_kwargs

Any Schema passed to use_kwargs MUST deserialize to a dictionary of data. If your schema has a post_load method that returns a non-dictionary, you should use use_args instead.

from marshmallow import Schema, fields, post_load
from webargs.flaskparser import use_args

class Rectangle:
    def __init__(self, length, width):
        self.length = length
        self.width = width

class RectangleSchema(Schema):
    length = fields.Float()
    width = fields.Float()

    def make_object(self, data, **kwargs):
        return Rectangle(**data)

def post(self, rect: Rectangle):
    return f"Area: {rect.length * rect.width}"

Packages such as marshmallow-sqlalchemy and marshmallow-dataclass generate schemas that deserialize to non-dictionary objects. Therefore, use_args should be used with those schemas.

Schema Factories

If you need to parametrize a schema based on a given request, you can use a “Schema factory”: a callable that receives the current request and returns a marshmallow.Schema instance.

Consider the following use cases:

  • Filtering via a query parameter by passing only to the Schema.

  • Handle partial updates for PATCH requests using marshmallow’s partial loading API.

from flask import Flask
from marshmallow import Schema, fields
from webargs.flaskparser import use_args

app = Flask(__name__)

class UserSchema(Schema):
    id = fields.Int(dump_only=True)
    username = fields.Str(required=True)
    password = fields.Str(load_only=True)
    first_name = fields.Str(missing="")
    last_name = fields.Str(missing="")
    date_registered = fields.DateTime(dump_only=True)

def make_user_schema(request):
    # Filter based on 'fields' query parameter
    fields = request.args.get("fields", None)
    only = fields.split(",") if fields else None
    # Respect partial updates for PATCH requests
    partial = request.method == "PATCH"
    # Add current request to the schema's context
    return UserSchema(only=only, partial=partial, context={"request": request})

# Pass the factory to .parse, .use_args, or .use_kwargs
@app.route("/profile/", methods=["GET", "POST", "PATCH"])
def profile_view(args):
    username = args.get("username")
    # ...

We can reduce boilerplate and improve [re]usability with a simple helper function:

from webargs.flaskparser import use_args

def use_args_with(schema_cls, schema_kwargs=None, **kwargs):
    schema_kwargs = schema_kwargs or {}

    def factory(request):
        # Filter based on 'fields' query parameter
        only = request.args.get("fields", None)
        # Respect partial updates for PATCH requests
        partial = request.method == "PATCH"
        return schema_cls(
            only=only, partial=partial, context={"request": request}, **schema_kwargs

    return use_args(factory, **kwargs)

Now we can attach input schemas to our view functions like so:

def profile_view(args):
    # ...

Custom Fields

See the “Custom Fields” section of the marshmallow docs for a detailed guide on defining custom fields which you can pass to webargs parsers:

Using the Method and Function fields requires that you pass the deserialize parameter.

@use_args({"cube": fields.Function(deserialize=lambda x: int(x) ** 3)})
def math_view(args):
    cube = args["cube"]
    # ...

Custom Parsers

To add your own parser, extend Parser and implement the load_* method(s) you need to override. For example, here is a custom Flask parser that handles nested query string arguments.

import re

from webargs import core
from webargs.flaskparser import FlaskParser

class NestedQueryFlaskParser(FlaskParser):
    """Parses nested query args

    This parser handles nested query args. It expects nested levels
    delimited by a period and then deserializes the query args into a
    nested dict.

    For example, the URL query params `?name.first=John&name.last=Boone`
    will yield the following dict:

            'name': {
                'first': 'John',
                'last': 'Boone',

    def load_querystring(self, req, schema):
        return _structure_dict(req.args)

def _structure_dict(dict_):
    def structure_dict_pair(r, key, value):
        m = re.match(r"(\w+)\.(.*)", key)
        if m:
            if r.get( is None:
                r[] = {}
            structure_dict_pair(r[],, value)
            r[key] = value

    r = {}
    for k, v in dict_.items():
        structure_dict_pair(r, k, v)
    return r

Returning HTTP 400 Responses

If you’d prefer validation errors to return status code 400 instead of 422, you can override DEFAULT_VALIDATION_STATUS on a Parser.

from webargs.falconparser import FalconParser

class Parser(FalconParser):

parser = Parser()
use_args = parser.use_args
use_kwargs = parser.use_kwargs

Bulk-type Arguments

In order to parse a JSON array of objects, pass many=True to your input Schema .

For example, you might implement JSON PATCH according to RFC 6902 like so:

from webargs import fields
from webargs.flaskparser import use_args
from marshmallow import Schema, validate

class PatchSchema(Schema):
    op = fields.Str(
        validate=validate.OneOf(["add", "remove", "replace", "move", "copy"]),
    path = fields.Str(required=True)
    value = fields.Str(required=True)

@app.route("/profile/", methods=["patch"])
def patch_blog(args):
    """Implements JSON Patch for the user profile

    Example JSON body:

        {"op": "replace", "path": "/email", "value": ""}
    # ...

Mixing Locations

Arguments for different locations can be specified by passing location to each use_args call:

# "json" is the default, used explicitly below
@app.route("/stacked", methods=["POST"])
@use_args({"page": fields.Int(), "q": fields.Str()}, location="query")
@use_args({"name": fields.Str()}, location="json")
def viewfunc(query_parsed, json_parsed):
    page = query_parsed["page"]
    name = json_parsed["name"]
    # ...

To reduce boilerplate, you could create shortcuts, like so:

import functools

query = functools.partial(use_args, location="query")
body = functools.partial(use_args, location="json")

@query({"page": fields.Int(), "q": fields.Int()})
@body({"name": fields.Str()})
def viewfunc(query_parsed, json_parsed):
    page = query_parsed["page"]
    name = json_parsed["name"]
    # ...

Next Steps