I have written a decorator that attempts to check we have post data for a Flask POST route:
Here's my decorator:
def require_post_data(required_fields=None):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
for required_field in required_fields:
if not request.form.get(required_field, None):
return jsonify({"error": "Missing %s from post data." %
required_field}), 400
else:
if not request.form:
return jsonify({"error": "No post data, aborting."}), 400
return f(*args, **kwargs)
return decorated_function
return decorator
And I have two routes, with with a URL param and the other without:
from flask import Blueprint, jsonify, request
mod = Blueprint('contacts', __name__, url_prefix='/contacts')
@mod.route('/', methods=['POST'])
@require_post_data(['customer_id', 'some_other_required_field'])
def create_contact():
# Do some business
@mod.route('/<int:contact_id>', methods=['POST'])
@require_post_data
def update_contact(contact_id):
# Do some business
When I run a test that hits update_contact
, I'm getting the following exception:
TypeError: decorator() got an unexpected keyword argument 'contact_id'
But it looks like create_contact
is working as expected.
Why is contact_id
being passed into decorator()
?
flask route paramsA parameter can be a string (text) like this: /product/cookie . So you can pass parameters to your Flask route, can you pass numbers? The example here creates the route /sale/<transaction_id> , where transaction_id is a number.
In some cases you can reuse a Flask route function for multiple URLs. Or you want the same page/response available via multiple URLs. In that case you can add a second route to the function by stacking a second route decorator to the function.
In your code, @app. route("/") is a decorator which adds an endpoint to the app object. It doesn't actually modify any behavior of the function, and is instead sugar to simplify the process. Without the route() decorator, this is how you'd do the equivalent route registration in Flask.
Routing in FlaskThe route() decorator in Flask is used to bind an URL to a function. As a result when the URL is mentioned in the browser, the function is executed to give the result. Here, URL '/hello' rule is bound to the hello_world() function.
I believe you're just missing one thing, which is to actually call require_post_data
to produce a decorator function in the update_contact
route. This should fix it:
@mod.route('/<int:contact_id>', methods=['POST'])
@require_post_data() # <- note the parens
def update_contact(contact_id):
# Do some business
The detailed explanation is that what you expected to happen (and what is happening in create contact
) is that the the view function is being modified by the decorator
produced by require_post_data
. In your update_contact
above, what is actually happening is that the view function is being passed to require_post_data
itself and simply used as the value of the required_fields
parameter. This doesn't cause an error so require_post_data
happily returns decorator
which is then routed to when you hit /<int>
, causing it to be passed contact_id
as a keyword argument, resulting in the error you saw.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With