未定义错误:'user' 未定义。

24

我目前正在开发一个Flask应用程序(已经进行了一年),遇到了一个相当奇怪的错误。我有一些文件始终包含在我的Jinja2模板中(导航栏),它们使用用户的名称和头像。因此,每次渲染模板时,我都会传递用户信息。最近,在我的生产服务器上注意到了一个错误:

<img alt="image" class="img-circle" src="{{ user.image }}" style="width: 48px;"/>
  File "/usr/local/lib/python2.7/dist-packages/jinja2/environment.py", line 397, in getattr
    return getattr(obj, attribute)
jinja2.exceptions.UndefinedError: 'user' is undefined

这是我的导航栏之一。渲染此模板的方法使用了以下内容:

@mod.route('/broken_pus', methods=['POST', 'GET'])
def view_broken_pus():
    return render_template("view_broken_pus.html", user=g.user, urls_for_active_clients=DeletedURLs.objects()[0].urls_for_active_clients, other_urls=DeletedURLs.objects()[0].other_urls)

正如您所看到的,我将user = g.user传递了下去。我在我的网站上的每个视图中都这样做。它在任何地方都有效,除了这个方法,它相当小。我有很多其他类似的路由,只有一个渲染模板,所以我不知道问题出在哪里。

我还在另一个更大的方法中遇到了这个问题,以前它总是有效的:

@mod.route('/users/add', methods=['GET', 'POST'])
@requires_roles("admin", "project-leader")
def add():
    """
    Method adding a new user.
    """
    # We do not use WTForms there since we need custom checkboxes for the role
    # Instead we use basic HTML and treat the checkboxes here
    if request.method == 'POST':
        user = User(name=request.form.get('name'),
                    email=request.form.get('email'))
        l = []
        # big switch assignement
        user.role = l
        try:
            user.save()
        except errors.NotUniqueError:
            flash(u'User %s already in database.' % user.name, 'danger')
            return redirect(url_for('home'))
        flash(u'User %s registered.' % user.name, 'success')
        return redirect(url_for('home'))
    return render_template('add_user.html', page=url_for('users.add'), user=g.user, clients=Client.objects())

当我第一次加载添加用户表单时,它可以正常工作。但是当我添加后,由于某种原因,我遇到了错误(用户没有保存到数据库中)。

由于在本地完美运行,我开始怀疑生产服务器本身存在问题。我们使用nginx和uwsgi来运行应用程序,并最近实施了一些Celery任务。你有什么想法吗?

提前致谢。


你能在 render_template 之前打印 g.user 看看会发生什么吗? - ljk321
好的,现在这变得有点奇怪了。我按照你说的做了,页面正常加载,尽管它没有显示太多内容,因为它使用的所有数据都在另外两个参数中。但是用户不再是问题了。只要我添加另外两个参数中的一个,它就会再次崩溃并出现用户错误!我尝试打印我的另外两个参数(包含元组的两个巨大列表),它们被正确填充并处于正常状态。有什么想法为什么它们会导致这个错误?特别是因为它在本地使用同样多的数据时可以工作。 - lap0573
不幸的是,uwsgi 在我们的生产服务器上似乎没有产生任何日志。我检查了 nginx 日志,它们是空的。我只有自己的 python.log,记录 Flask 遇到的所有错误。 - lap0573
你说得对,我尝试传递了list[:30]而不是整个列表,它起作用了。所以这绝对是一个列表太大的问题...虽然我必须说我很惊讶,因为我传递了这两个列表:
  • urls_for_active_clients,其中有大约400个元组(2个对象和一个字符串的元组),我在<table>中显示它们
  • other_urls,其中有大约3000个元组(类似的),我还没有显示它们。
所以实际上,是我显示的东西导致了它崩溃,而不是我传递列表的事实!因为我没有显示这个巨大的列表,所以它可以工作。
- lap0573
1
我建议使用上下文处理器,这样一个全局对象就可以在所有模板中使用,而不必传递它:http://flask.pocoo.org/docs/0.10/templating/#context-processors虽然这可能不能直接解决您的问题,但它将帮助您保持代码更清洁、更易于调试。您可以在我正在开发的项目中看到一个示例: 主程序:https://github.com/kerryhatcher/sdf-tools/blob/master/SDFtools/__init__.py 认证模块:https://github.com/kerryhatcher/sdf-tools/blob/master/SDFtools/auth/context.py - Kerry Hatcher
显示剩余13条评论
2个回答

1

查看Flask render_template源代码

它只是调用template.render(context),但在调用before_render_template.send(app, template=template, context=context)之后。

从这里可以看出,有一些before_render_template处理程序,可以修改已安装的上下文。

为了调试此问题,我可以尝试调用类似于以下内容的东西:

from flask import app

@mod.route('/broken_pus', methods=['POST', 'GET'])
def view_broken_pus():
    template = app.jinja_env.get_or_select_template("view_broken_pus.html")
    return template.render(dict(
        user=g.user,
        urls_for_active_clients=DeletedURLs.objects()[0].urls_for_active_clients,
        other_urls=DeletedURLs.objects()[0].other_urls,
    ))

如果这个可以正常工作,我将需要深入了解谁在before_render_template插槽中修改上下文。

0

我怀疑是线程问题。如果g是某种全局引用,那么您可能需要确保它在threading.local上设置或使用线程锁来确保没有线程可以在某个“其他”线程干扰它之前获取g.user。

请参见如何使2.7 Python上下文管理器线程安全,以一种不牺牲线程安全性的方式处理“全局变量”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接