Django 从 1.7 升级到 1.8:视图中需要来自上下文处理器的变量

Django upgrade from 1.7 to 1.8: Need variable from contextprocessor in view

许多变量必须在每次页面点击期间确定,但在我的项目的不同地方使用,不仅是模板,而且在视图中。到目前为止,我一直在使用上下文处理器(称为 'globals')来实现此结果。请注意,在上下文处理器中,我正在进行实际的计算和数据库调用,因此我不仅仅需要一个设置变量。

自从 Django 1.7 升级到 1.8 后,上下文处理器返回的变量仍然显示在模板中,这很好,但它们不再显示在视图中,至少我找不到它们。

在我的上下文处理器中,我有以下代码:

def globals(request):

    # NOTE: We DON'T simply need a variable from settings - in reality this is computed
    if_this_is_true_then_we_alter_text = True

    ctx = {
        'var_from_contextprocessor': if_this_is_true_then_we_alter_text,
    }

    return ctx

那么,在我看来,我有:

from django.template import RequestContext
from django.shortcuts import render

def show_globals(request):
    ctx = RequestContext(request)
    ctx['var_from_view'] = 'YES, we found it!' if ctx.has_key('var_from_contextprocessor') else 'NO, it ain\'t there!'
    return render(request, 'show_globals.html', context_instance=ctx)

我的模板,show_globals.html如下:

var_from_view: {{ var_from_view }}
var_from_contextprocessor: {{ var_from_contextprocessor }}

当 运行 Django 1.7 时,视图模板中的输出将为 "YES, we found it!"。但是,一旦我升级到 1.8,上下文处理器返回的变量似乎可用于视图,因此文本更改为 "NO, it ain't there!"。然而,在这两种情况下,var_from_contextprocessor 都会适当地显示在模板本身中。

还有办法在单个视图中从上下文处理器中检索变量吗?如果没有,关于如何在不使用上下文处理器的情况下获得相同结果的任何建议?

请注意,我试图解决的基本问题只是在每次页面点击期间即时计算变量,然后这些变量可用于视图和模板。我真的不在乎这是否是通过使用上下文处理器完成的。

提前致谢!

1.7 和 1.8 之间的变化是引入了不同的模板后端,以允许 first-class 支持例如 Jinja2。这意味着上下文处理器已经从 top-level 设置转变为与特定模板后端关联的设置;例如,TEMPLATE_CONTEXT_PROCESSORS 设置已弃用,但目前仍受支持。

这样做的结果是 RequestContext 在与实际模板相关联之前不与任何特定的上下文处理器集相关联。默认情况下,当您调用模板的 render 方法(它本身由上面使用的独立 render 快捷方式调用)时会发生这种情况。

但是,您可以自己执行此操作,方法是显式调用 bind_template 并在视图中自己进行渲染:

from django.template import loader

ctx = RequestContext(request)
tpl = loader.get_template('show_globals.html')
with ctx.bind_template(tpl.template):
    ctx['var_from_view'] = 'YES, we found it!' if ctx.has_key('var_from_contextprocessor') else 'NO, it ain\'t there!'
    return HttpResponse(template.render(ctx))

不过我必须说,这整件事让我觉得是对上下文处理器的误用,出于某种原因将其称为 template 上下文处理器。您应该考虑重新编写代码,以便在视图中不需要此访问权限 - 也许在模板标记中执行此操作?

我发现了一个有帮助的解决方案,至少对我来说是这样。

所以期望的结果是在每次页面加载时都有代码 运行,结果在所有视图和所有模板中都可用。

在 Django 版本 1.7 和 1.8 之间停止使用简单的上下文处理器的技术原因在 Daniel Roseman 的不同答案中得到了很好的解释,所以我不会在这里讨论它们。

长话短说,诀窍是使用 中间件 而不是 上下文处理器 ,然后 contextprocessormiddleware.

继承变量

考虑原题中的代码,与新代码对比:

上下文处理器:

def globals(request):

    ctx = request.extravars # See example.middleware.ExtraVarsMiddleware

    return ctx

中间件:

class ExtraVarsMiddleware():
    # For handing certain variables over to the context processor for global display.
    def process_view(self, request, view_func, view_args, view_kwargs):

        # NOTE: We DON'T simply need a variable from settings
        if_this_is_true_then_we_alter_text = True

        request.extravars = {
            'var_from_contextprocessor': if_this_is_true_then_we_alter_text,
        }

"global" 变量挂在一个名为 extravars 的任意命名字典中的请求对象上。请注意,虽然我们的 "global" 变量条目仍称为 var_from_contextprocessor,但此时用词不当,因为它来自中间件,不再是上下文处理器。

为了激活这个中间件,必须更改设置以包含它,就像这样(包含默认值):

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',

    'example.middleware.ExtraVarsMiddleware',
)

然后模板可以像以前一样访问变量,因为上下文处理器已将整个 request.extravars 添加到其生成的上下文中,然后按照上面的代码返回。

然后,为了访问视图中的变量,我们需要对之前的内容稍作改动:

from django.shortcuts import render

def show_globals(request):
    ctx = request.extravars
    ctx['var_from_view'] = 'YES, we found it!' if ctx.has_key('var_from_contextprocessor') else 'NO, it ain\'t there!'
    return render(request, 'show_globals.html', context=ctx)

我已尽可能少地更改此代码以演示所需的最少更改。只有两件事发生了变化,一个是 ctx 字典的获取方式,这次没有使用 RequestContext。第二个变化是我们不再将 context_instance 传递给 render 函数,而是简单地传递 context.

为澄清起见,我的示例设置中相应的文件名是:

example/contextprocessors.py
example/middleware.py
example/views.py
settings.py