wont_compile wont_compile - 2 months ago 24
Python Question

Inconsistent globals in Flask

I have a menu where users can see different menu items depending on their permissions. I check whether the users has the option or not when logging in, store it in a global variable, and check that variable when rendering the menu.

In production, the menu only shows up correctly about 50% of the time. Sometimes the value is set, sometimes it is empty. Why isn't this working correctly?

@app.route('/login/', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
user = User.get(request.form['username'])

if user.user is None:
return redirect('/login/')

if user and check_password_hash(user.user.password, request.form['password']):
login_user(user)
if isinstance(current_user.user, UserTypeOne):
group = UserGroup.query.filter_by(id=int(current_user.user.group)).first()
app.jinja_env.globals['group_access_to_feature_one'] = group.group_access_to_feature_one

return redirect(request.args.get('next') or url_for('index'))

return redirect(url_for('login'))


The
header
template has a conditional to check that value:

{% if group_access_to_feature_one%}<ul>...</ul>{%endif%}


When debugging, I put
{{ group_access_to_feature_one }}
to see the value, and sometimes it is ˙True` and sometimes it's empty.

Answer

In production (and possibly sometimes on dev), you are running with multiple processes. Each process creates its own copy of the app, and so only the app in the process that handled the login request will see the changes to the env. This is one of the main reasons using Python globals to store state is discouraged. app.jinja_env.globals is meant to be modified only during setup so that each process/thread remains consistent.

Use a database or other storage such as redis to store and access global state. Use the session to store information about a specific browser session, such as the logged in user. Load the state for each user on each request using an app.before_request callback.

Your code is also incorrect because the global Flask environment is changed every time a user logs in. So the value of the value for every user will be based on the last user to log in and set the value.