小能豆

Flask 中不一致的全局变量

py

我有一个菜单,用户可以根据自己的权限看到不同的菜单项。我在登录时检查用户是否有该选项,将其存储在全局变量中,并在呈现菜单时检查该变量。

在生产中,菜单只有大约 50% 的时间能正确显示。有时设置了值,有时为空。为什么它不能正常工作?

@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'))

模板header有一个条件来检查该值:

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

在调试的时候,我把{{ group_access_to_feature_one }}值放进去看,有时候是有True,有时候是空的。


阅读 25

收藏
2024-12-03

共1个答案

小能豆

根据您的描述,问题出现在 app.jinja_env.globals['group_access_to_feature_one'] 有时被正确设置为 True,有时为空。这个问题可能与以下几个因素相关:

可能的原因:

  1. 会话管理问题
    您在 login() 路由中设置了 group_access_to_feature_one,但是这个值是设置在全局 Jinja 环境中 (app.jinja_env.globals) 的。如果页面在不同的请求周期中被访问(例如刷新页面、跳转等),并且没有正确传递这个值,您就会遇到该值丢失或不一致的情况。

  2. group_access_to_feature_one 值的生命周期问题
    app.jinja_env.globals 中的全局变量并不会自动与每个用户的会话(session)关联。如果不同的用户登录,可能存在多个会话之间的干扰,或者在某些情况下,用户的 group_access_to_feature_one 被设置为空。

  3. 权限设置不一致
    另外一个潜在问题是 current_user 的权限信息可能在某些请求周期中不可用,导致 group_access_to_feature_one 没有被正确设置。

解决方法:

1. 使用会话(session)存储权限信息

为了避免全局变量可能带来的问题,可以使用 session 来存储用户的权限信息,确保每个用户有自己的独立会话数据。

from flask import session

@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()
                session['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'))

然后在模板中,您可以通过 session 获取该值:

{% if session['group_access_to_feature_one'] %}
    <ul>...</ul>
{% endif %}

2. 传递变量到模板

另一种方法是在渲染页面时,直接将该权限信息作为上下文变量传递给模板,而不是使用全局变量。这样确保每次请求都有正确的数据。

@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)
            group_access_to_feature_one = False
            if isinstance(current_user.user, UserTypeOne):
                group = UserGroup.query.filter_by(id=int(current_user.user.group)).first()
                group_access_to_feature_one = group.group_access_to_feature_one

            return render_template('index.html', group_access_to_feature_one=group_access_to_feature_one)

        return redirect(url_for('login'))

然后在模板中访问这个变量:

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

3. 确保 group_access_to_feature_one 的访问逻辑没有问题

有时候可能是 group_access_to_feature_one 没有被正确赋值。检查 group.group_access_to_feature_one 是否在 group 对象中确实存在,并且赋值逻辑是正确的。


总结一下:最稳妥的方式是使用 session 来存储每个用户的权限信息,或者确保在渲染模板时直接传递正确的权限信息。这样可以避免因为全局变量或状态不一致导致的权限检查问题。

2024-12-03