Search code examples
pythonflaskdecoratorpython-decorators

Flask decorator : Can't pass a parameter from URL


i'm quite new with flask and i'm trying to use the migthy power of decorators :p I read lot of things and found tons of topics about python decorators here but nothing really helpful.

@app.route('groups/<id_group>')
@group_required(id_group)
@login_required
def groups_groupIndex(id_group):
    #do some stuff
    return render_template('index_group.html')

This is the error i get :

@group_required(id_group), NameError: name 'id_group' is not defined

Ok, id_group is not defined yet, but I don't understand why i CAN use the id_group parameter from the URL in the function groups_groupIndex but NOT in the decorator!

I try to move/switch decorators, but the same error occure each time.

Here is my decorator, but it seems to work fine

def group_required(group_id):
    def decorated(func):
        @wraps(func)
        def inner (*args, **kwargs):
            #Core_usergroup : table to match users and groups
            groups = Core_usergroup.query.filter_by(user_id = g.user.id).all()
            for group in groups:
                #if the current user is in the group : return func
                if int(group.group_id) == int(group_id) :
                    return func(*args, **kwargs)
            flash(gettext('You have no right on this group'))
            return render_template('access_denied.html')     
        return inner
    return decorated

Maybe i don't see decorators like i should... Can i use my decorator this way or need i rewrite something different ?


Solution

  • You defined group_id as a function parameter; that makes it a local name in that function.

    That doesn't make the name available to other scopes; the global namespace that the decorator lives in cannot see that name.

    The wrapper function, however, can. It'll be passed that parameter from the @apps.route() wrapper when called:

    def group_required(func):
        @wraps(func)
        def wrapper(group_id, *args, **kwargs):
            #Core_usergroup : table to match users and groups
            groups = Core_usergroup.query.filter_by(user_id = g.user.id).all()
            for group in groups:
                #if the current user is in the group : return func
                if int(group.group_id) == int(group_id) :
                    return func(*args, **kwargs)
            flash(gettext('You have no right on this group'))
            return render_template('access_denied.html')     
        return wrapper
    

    Note that this decorator doesn’t bother with passing on the group_id parameter to the decorated function; use return func(group_id, *args, **kwargs) instead of you still need access to that value in the view function.