I'm using Flask and have two decorators that I'm trying to use on certain routes to be more Pythonic, prevent code reuse, and improve readability.
The first of these are just checks to see if the user is logged in else redirect user to the login page. This works fine.
After this I check for the user 'class' (Admin, Manager, Standard User, etc), however this one isn't working and I'm not sure what is missing.
From the routes.py:
#app.route('/user/user-account.html', methods=['GET', 'POST'])
#login_required
def cna_account():
if valid_user() == True:
result = get_user_account()
return render_template('user/user-account.html', result=result)
else:
return redirect(url_for('index'))
#login_reqiured works fine, here is the code:
#wraps(f)
def wrap(*args, **kwargs):
if 'logged_in' in session:
return f(*args, **kwargs)
else:
return redirect(url_for('index'))
return wrap
What I tried for the other decorator that doesn't work:
def valid_user(f):
''' Makes sure that only Base Users can view the Base User pages '''
#wraps(f)
def wrap(*args, **kwargs):
if 'access' in session and session['access'] == 'c':
return f(*args, **kwargs)
else:
return redirect(url_for('index'))
These are stored in a separate module that is imported in routes.py, the only thing I can guess is perhaps the session isn't being carried across even though it's included from flask in the module and routes, but again I'm not sure how this would be remedied.
What I'm trying to do with it is: have the routes that use both decorators and don't require the if valid_user() == True check. Instead it should function and look like:
#app.route('/user/user-account.html', methods=['GET', 'POST'])
#login_required
#valid_user
def cna_account():
result = get_user_account()
return render_template('user/user-account.html', result=result)
Any help on what I'm missing here? Do I need to pass the session variable as an argument to #valid_user? I tried that a few different ways and still had it throw errors.
Much appreciated!
You need to return the inner function wrap in valid_user:
def valid_user(f):
#wraps(f)
def wrap(*args, **kwargs):
if 'access' in session and session['access'] == 'c':
return f(*args, **kwargs)
return redirect(url_for('index'))
return wrap
While it is perfectly valid to not return anything from an outer decorator function, bear in mind that the wrapped function is passed to the wrapper at run time, thus, the returned value will be None:
def foo(f):
print("inside decorator with '{}'".format(f.__name__))
def inner():
return 10
#foo
def bar():
return 'in bar'
"inside decorator with 'bar'"
>>>bar()
Traceback (most recent call last):
File "", line 1, in
TypeError: 'NoneType' object is not callable
Related
currently I have this code
#app.route("/protect1")
def protect1():
if not session.get('logged_in'):
session['next'] = "/protect1"
return render_template('login.html')
else:
return "This is the first protected page protect1"
#app.route("/protect2")
def protect2():
if not session.get('logged_in'):
session['next'] = "/protect2"
return render_template('login.html')
else:
return "This is the second protected page protect2"
in my flask app, everything working fine. Only it is not nice that I will need to repeat for each function (view) the if/else combination.
I would prefer to have some generic way, like this pseude-code:
#checklogin
#app.route("/protect1")
def protect1():
return "This is the first protected page protect1"
#checklogin
#app.route("/protect2")
def protect2():
return "This is the second protected page protect2"
One challenge here is that the #checklogin decorator would need to know the app.route path (e.g. "/protect1") in order to be able to set session['next'] correctly. I have no idea how to pass this parameter to the decorator, especially how to find it out in the first place. In other words, how does the function protect1() know that it is decorated with #app.route and which parameter ("/protect1") has been passed to that app.route decorator?
The decorator can look up the path on request; either using the URL that was loaded (available as request.url) or the request.endpoint attribute:
from functools import wraps
from flask import request, session
def checklogin(f):
#wraps(f)
def wrapper(*args, **kwargs):
if not session.get('logged_in'):
session['next'] = request.url
return render_template('login.html')
return f(*args, **kwargs)
return wrapper
Do place the decorator after the app.route() decorator or it'll not be registered as the handler for the route:
#app.route("/protect1")
#checklogin
def protect1():
return "This is the first protected page protect1"
i want to protect page on python-flask, can go on this page only limited users.
I'm write this code :
def im_seller():
if not g.user.seller_fee_paid:
return redirect(url_for('account.seller_fee'))
return
#account.route('/account/seller/products.html')
#login_required
#im_seller
def seller_products():
the script not working, give this error :
TypeError: im_seller() takes no arguments (1 given)
where I'm wrong ? thanks all.
Decorators take a function and need to return a function:
from functools import wraps
def require_seller(f):
#wraps(f)
def require_seller_wrapper(*args, **kwargs):
if not g.user.seller_fee_paid:
return redirect(url_for('account.seller_fee'))
return f(*args, **kwargs)
return require_seller_wrapper
You will also want to reverse the order of require_seller and login_required so you can be sure that g.user is set:
#account.route('/account/seller/products.html')
#require_seller
#login_required
def seller_products():
return "All the seller's products"
See this answer about decorators for all the details as to the whys.
from functools import wraps
def require_seller(f):
#wraps(f)
def require_seller_wrapper(*args, **kwargs):
if not g.user.seller_fee_paid:
return redirect(url_for('account.seller_fee'))
return f(*args, **kwargs)
return require_seller_wrapper
You should use the python decorator.Link
I defined a check user method:
from functools import wraps
def check_user(func):
#wraps(func)
def wrapper(*args, **kwargs):
if session['logged_in']:
return func(*args, **kwargs)
else:
return 'Log in'
return wrapper
#app.route('/test')
#check_user
def test():
return "Hello"
It does not work. how can I correct it?
It seems you don't know how to create decorators in python. There are many helpful answers on this question: How can I make a chain of function decorators in Python?
Below is how you can create a decorator that checks if a user is logged in.
from functools import wraps
def checkuser(func):
"""Checks whether user is logged in or raises error 401."""
#wraps(func)
def wrapper(*args, **kwargs):
if not g.user:
abort(401)
return func(*args, **kwargs)
return wrapper
The decorator above will raise a 401 error if a user is not logged in. It will return the view function otherwise.
This has been driving me crazy because it should be so simple, but there must be some Python quirk I'm missing. I have a decorator that I'm trying to apply to a Flask route, but for some reason none of the decorators in my views.py seem to be getting loaded.
decorators.py
def admin_required(func):
"""Require App Engine admin credentials."""
#wraps(func)
def decorated_view(*args, **kwargs):
if users.get_current_user():
if not users.is_current_user_admin():
abort(401) # Unauthorized
return func(*args, **kwargs)
return redirect(users.create_login_url(request.url))
return decorated_view
views.py
#admin_required
#blueprint.route('/')
def index():
return render_template('index.html')
The admin_required decorator function is not being called (index.html is loaded without a redirect), and I cannot figure out why.
Short answer: change the order of the decorators; blueprint.route only "sees" your undecorated function.
Decorators are applied inside-out, in loose analogy to function calls. Thus your function definition is equivalent to:
def index():
return render_template('index.html')
index = blueprint.route('/')(index)
index = admin_required(index)
Note how blueprint.route is passed the index function before it gets wrapped by admin_required. Of course, admin_required does eventually get applied to the index name in the module, so if you were to call index directly, it would go through both decorators. But you're not calling it directly, you're telling flask's request processor to call it.
Can someone please explain to me the difference between these two blocks of code. The first one works while the latter throws the error which I've indicated in the title.
def login_required(method):
#functools.wraps(method)
def wrapper(*args, **kwargs):
if 'username' in flask.session:
return method(*args, **kwargs)
else:
flask.flash("A login is required to see the page!")
return flask.redirect(flask.url_for('index'))
return wrapper
AND
def login_required(method):
#functools.wraps(method)
def wrapper(*args,**kwargs):
if "username" in flask.session:
return method(*args,**kwargs)
else:
flask.flash("A login is required to see the page!")
return flask.redirect(flask.url_for('index'))
return wrapper
In the first code sample, you correctly return the wrapper function at the end of the login_required function.
In the second code sample you've got the return wrapper inside the wrapper function itself. Just de-dent that last line and you should be all set.