python-restx / flask-restx

Fork of Flask-RESTPlus: Fully featured framework for fast, easy and documented API development with Flask
https://flask-restx.readthedocs.io/en/latest/
Other
2.16k stars 335 forks source link

None is not of type 'number' #554

Closed miaokela closed 1 year ago

miaokela commented 1 year ago

When I use float type in model, the field value carried in the send request is None, model validation is fine, but what is this document validation? Why can't you pass?

13 12 image
peter-doggart commented 1 year ago

Can you provide a minimal example of this issue? I can pass a float to a model fine:

from flask import Flask, Blueprint, request
from flask_restx import Api, Resource, fields

api_v1 = Blueprint("api", __name__, url_prefix="/api/1")

api = Api(
    api_v1,
    version="1.0",
    title="Todo API",
    description="A simple TODO API",
)

ns = api.namespace("todos", description="TODO operations")

todo = api.model(
    "Todo", {"threshold": fields.Float(required=True, description="A float value")}
)

parser = api.parser()
parser.add_argument(
    "threshold", type=float, required=True, help="A float value", location="json"
)

@ns.route("/threshold")
class Test(Resource):
    """Put a single item with float"""
    @api.expect(todo, validate=True)
    def post(self):
        """Update a given resource"""
        args = parser.parse_args()

        print(f"You can access the float via the parser: {args['threshold']}")
        print(f"Or you can access it from the request: {request.json['threshold']}")

        return "Ok", 200

if __name__ == "__main__":
    app = Flask(__name__)
    app.register_blueprint(api_v1)
    app.run(debug=True)

Running:

curl -X 'POST' \
  'http://127.0.0.1:5001/api/1/todos/threshold' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "threshold": 475.12
}'

Prints:

You can access the float via the parser: 475.12
Or you can access it from the request: 475.12
miaokela commented 1 year ago
image

Take your example, Float type has no way to submit the None value, what can be adjusted to allow this null value, 0 May be syntactically null for Float, but 0 May make sense for practical situations, such as the temperature will be lower than 0, I use 0 as a threshold, not no temperature

peter-doggart commented 1 year ago

As you say, this is correct because None is not a valid Float value and JSON doesn't have a nice way to pass NaN which would be a valid float.

My suggestion would be, since passing null is effectively the same as not passing a value, make the field optional then infer in code if the user set it or not with something like:

request.json.get('threshold', np.nan)
miaokela commented 1 year ago
image

Perhaps rewrite the double underscore method is not reasonable, in view of the scenario requirements, or rewrite flask_restx.Resource.__validate_payload, there will be None value of the data is not checked, so as to solve the current problem, thank you for your help