从 Flask Navigation 访问 Flask Session 变量以获取动态导航菜单

Accessing Flask Session variables from Flask Navigation for dynamic navigation menu

我想要一个动态导航菜单,如果用户当前未登录则显示 "Login",如果用户已登录则显示 "Logout"。

我使用的代码类似于以下内容:

import flask
import flask_nav
import flask_nav.elements as fne

frontend = flask.Blueprint('frontend', __name__)

application = flask.Flask(__name__)
mySess = flask_session.Session()

flask_appconfig.AppConfig(application)
flask_bootstrap.Bootstrap(application)
application.register_blueprint(frontend)
application.config['BOOTSTRAP_SERVE_LOCAL'] = True
application.config['SSL'] = True
application.secret_key = SECRET_KEY
application.config['SESSION_TYPE'] = SESSION_TYPE

mySess.init_app(application)

nav = flask_nav.Nav()

class CustomRenderer(flask_bootstrap.nav.BootstrapRenderer):
    def visit_Navbar(self, node):
        nav_tag = super(CustomRenderer, self).visit_Navbar(node)
        nav_tag['class'] = 'navbar navbar-default navbar-fixed-top'
        return nav_tag

flask_nav.register_renderer(application, 'custom', CustomRenderer)

nav.init_app(application)

@nav.navigation()
def top_nav():
    items = [ fne.View('Home',              '.index') ]

    if 'google_token' in flask.session:
        items.append(fne.View('Logout',         '.logout'))
    elif 'auth_url' in flask.session:
        items.append(fne.View('Login',          flask.session['auth_url']))
    else:
        items.append(fne.View('Login',          '.login'))

    items.append(fne.View('About',              '.about'))
    items.append(fne.View('Contact',            '.contact'))
    items.append(fne.View('Shop',               '.shop'))
    items.append(fne.View('Help & Feedback',    '.help'))

    return fne.Navbar('', *items)

nav.register_element('frontend_top', top_nav())

不幸的是,Flask 会话变量超出了导航对象的范围,因此我无法从 top_nav.

中访问 flask.session

当我在我的应用程序之外创建任何独立函数来访问 flask-session 时,我遇到了同样的困难,例如

def user_is_logged_in():
    if 'google_token' in flask.session:
        return True
    else:
        return False
    return False

这些函数给出了预期的误差"RuntimeError: Working outside of request context."

出于安全原因,我不想在我的 application.py 代码中为用户使用全局变量,这样多人可以同时访问该应用程序而不会出错。我相信 SESSION 应该存储用户当前是否登录。

如何让我的 flask_nav.Nav() 看到我的应用程序的 flask.session?

Flask-Login 会让您的生活更轻松。它提供了一个 current_user 指向当前用户,用户对象有一个 is_authenticated 属性:

from flask_login import current_user
...
@nav.navigation()
def top_nav():
    ...
    if current_user.is_authenticated: 
        items.append(View("Logout", ".logout")) 
    else: 
        items.append(View("Login", ".login"))

初始化 Flask-Login 的代码如下:

from flask import Flask
from flask_login import LoginManager, UserMixin

app = Flask(__name__)
login_manager = LoginManager(app)

# The user model
class User(db.Model, UserMixin):
    ...


@login_manager.user_loader
def load_user(user_id):
    return User.get(user_id)

查看 the documentation 了解更多详情。

flask_nav 在开始处理请求之前的应用程序生命周期中的某个阶段注册扩展。

当应用程序中存在请求上下文时,您可以稍后覆盖 template_global 的注册。

排除常见的导航项。

nav = Nav()

# registers the "top" menubar
navitems = [
    View('Widgits, Inc.', 'index'),
    View('Our Mission', 'about'),
]

根据会话

中的值将函数设置为return适当的View/Link
def with_user_session_action(items):
    return (
        items 
        + [ View('Login', 'login') if not session.get('logged') else View('Logout', 'logout')]
    )

在委托给 nav.register_element

的函数中使用它
def register_element(nav, navitems):
    navitems = with_user_session_action(navitems)
    return nav.register_element('top', 
        Navbar(*navitems)
    )

取代render_template以始终传递计算的导航

_render_template = render_template

def render_template(*args, **kwargs):
    register_element(nav, navitems)

    return _render_template(*args, nav=nav.elems, **kwargs)

奖金:

您可以为 login/logout 缓存计算的导航,这样它就不会只为每个案例计算一次。