0

I have a simple Users resource with a put method to update all user information except user password. According to Flask-Restx docs when a model has set the strict and validation params to true, a validation error will be thrown if an unspecified param is provided in the request. However, this doesn't seem to be working for me.

Model definition:

from flask_restx import Namespace, Resource, fields, marshal

users_ns = Namespace("users")

user = users_ns.model(
    "user",
    {
        "user_name": fields.String(example="some_user", required=True),
        "email": fields.String(example="some.user@email", required=True),
        "is_admin": fields.Boolean(example="False"),
        "is_deactivated": fields.Boolean(example="False"),
        "created_date": fields.DateTime(example="2020-12-01T01:59:39.297904"),
        "last_modified_date": fields.DateTime(example="2020-12-01T01:59:39.297904"),
        "uri": fields.Url("api.user"),
    },
    strict=True,
)

user_post = users_ns.inherit(
    "user_post", user, {"password": fields.String(required=True)}
) # Used for when 

Resource and method definition:

from api.models import Users

class User(Resource):
    @users_ns.marshal_with(user)
    @users_ns.expect(user, validate=True)
    def put(self, id):
        """
        Update a specified user.
        """

        user = Users.query.get_or_404(id)
        body = request.get_json()

        user.update(body)

        return user

Failing Test:

def test_update_user_invalid_password_param(self, client, db):
        """ User endpoint should return 400 when user attempts to pass password param to update. """
        data = {
            "user_name": "some_user",
            "email": "some.user@email.com",
            "password": "newpassword",
        }

        response = client.put(url_for("api.user", id=1), json=data)

        assert response.status_code == 400

The response.status_code here is 200 because no validation error is thrown for the unspecified param passed in the body of the request.

Am I using the strict param improperly? Am I misunderstanding the behavior of strict?

UPDATED: I've added the test for strict model param from Flask-RestX repo (can be found here) for more context on expected behavior:

def test_api_payload_strict_verification(self, app, client):
        api = restx.Api(app, validate=True)
        ns = restx.Namespace("apples")
        api.add_namespace(ns)

        fields = ns.model(
            "Person",
            {
                "name": restx.fields.String(required=True),
                "age": restx.fields.Integer,
                "birthdate": restx.fields.DateTime,
            },
            strict=True,
        )

        @ns.route("/validation/")
        class Payload(restx.Resource):
            payload = None

            @ns.expect(fields)
            def post(self):
                Payload.payload = ns.payload
                return {}

        data = {
            "name": "John Doe",
            "agge": 15,  # typo
        }

        resp = client.post_json("/apples/validation/", data, status=400)
        assert re.match("Additional properties are not allowed \(u*'agge' was unexpected\)", resp["errors"][""])
CLPatterson
  • 113
  • 1
  • 14
  • I know there are other options to solve this problem, such as using ReqParser, but I'm specifically interested in the strict model param and its behavior. – CLPatterson Dec 10 '20 at 16:36

2 Answers2

3

I resolved my issue by pulling the latest version of Flask-RESTX from Github. The strict parameter for models was merged after Flask-RESTX version 0.2.0 was released on Pypi in March of 2020 (see the closed issue in Flask-RESTX repo for more context). My confusion arose because the documentation appears to represent the latest state of master and not the last Pypi release.

CLPatterson
  • 113
  • 1
  • 14
0

It's been a while since I touched on this but from what I can tell, I don't think you are using the strict param correctly. From the documentation here, the :param bool strict is defined as

:param bool strict: validation should raise an error when there is param not provided in schema

But in your last snippet of code, you are trying to validate with the dictionary data with the body of the request.

If I recall well again, for this sort of task you need to use (and as you mentioned) a RequestParser. There's a good example of it here - flask - something more strict than @api.expect for input data?

AzyCrw4282
  • 7,222
  • 5
  • 19
  • 35
  • Thanx for the response. So in the example test I've provided, 'data' is passed in the body of my request to the User put method. The User put method validates input against my user model. The user model does not include a 'password' field, so when using strict I'd expect the request with 'data' json body to be rejected with something like the error message from the test in Flask-Restx repo (added to my question above). Should return 400 with an error message like "Additional properties are not allowed..." – CLPatterson Dec 17 '20 at 15:31
  • @CLPatterson looked into it but i couldn't find the cause of the error. Maybe, you can use the alternative method or ask the question again to attract some fresh new eyes. – AzyCrw4282 Dec 22 '20 at 03:59