Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass Flask route parameters into a decorator

Tags:

python

flask

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()?

like image 499
Chris McKinnel Avatar asked Sep 04 '13 05:09

Chris McKinnel


People also ask

How do you pass a parameter to a route in Flask?

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.

Can you use multiple decorators to route URLs to a function in Flask?

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.

Is App route a decorator?

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.

How do you route a 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.


1 Answers

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.

like image 144
James Porter Avatar answered Sep 30 '22 13:09

James Porter