Search code examples
pythonflaskmarshmallowhttp-status-code-422webargs

Unable to validate list of dicts using marshmallow Schema with webargs use_kwargs - Error 422 Unprocessable Entity


I have the following endpoint in Flask:

from flask import Flask
from marshmallow import EXCLUDE, Schema, fields
from webargs.flaskparser import use_kwargs

app = Flask(
    __name__, static_url_path="", static_folder="static", template_folder="templates"
)


class Measure(Schema):
    type = fields.Int(required=True)
    value = fields.Float(required=True)
    unit = fields.Int(required=True)
    date = fields.Int(required=True)


class RequestSchema(Schema):
    measures = fields.List(fields.Nested(Measure))


@app.route("/save", methods=["POST"])
@use_kwargs(
    RequestSchema,
    location="json_or_form",
    unknown=EXCLUDE,
)
def save(**measures):
    return {"foo": measures}, 200

With pytest, I test the following:

import pytest

@pytest.fixture(scope="module")
def app():
    test_app = AppClientPublic()
    return test_app.app

def test_save(app):
    measures = [
        {
            "type": 1,
            "value": 2.2,
            "unit": 3,
            "date": 4,
        },
        {
            "type": 5,
            "value": 6.6,
            "unit": 7,
            "date": 8,
        },
    ]
    r = app.post(
        "/save",
        data={"measures": measures},
    )
    assert r.status_code == 200

I get the following error:

Error 422 Unprocessable Entity: The request was well-formed but was unable to be followed due to semantic errors. - {'messages': {'json_or_form': {'measures': {0: {'_schema': ['Invalid input type.']}, 1: {'_schema': ['Invalid input type.']}}}}, 'schema': <RequestSchema(many=False)>, 'headers': None}

However, if I validate the same dictionary directly:

d = {
    "measures": [
        {
            "type": 1,
            "value": 2.2,
            "unit": 3,
            "date": 4,
        },
        {
            "type": 5,
            "value": 6.6,
            "unit": 7,
            "date": 8,
        },
    ]
}

req_schema = RequestSchema()
errors = req_schema.validate(d)
print(errors)  # {} -> no errors

I get no validation error.

I also have absolutely no problem validating simple objects like just a depth-1 dictionary, but I am unable to validate a list of Measure on the Flask endpoint.

What am I doing wrong?

PS:


Solution

  • The problem was on the pytest side, not the Flask side.

    I changed the post call to use json instead of data:

    r = app.post("/save", json={"measures": measures})
    # instead of 
    r = app.post("/save", data={"measures": measures})