Django从1.7升级到1.8:在视图中需要来自contextprocessor的变量

时间:2015-12-28 21:24:05

标签: django django-templates django-1.8

在每个页面命中期间必须确定许多变量,但在项目的不同位置使用,不仅是模板,还包括视图。到目前为止,我一直在使用上下文处理器(称为' 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时,视图中模板的输出将是"是的,我们找到了它!"。但是,一旦我升级到1.8,上下文处理器返回的变量似乎可供视图使用,因此文本更改为" NO,它就在那里!&#34 ;。在这两种情况下,var_from_contextprocessor都会在模板中正确显示。

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

请注意,我尝试解决的基本问题只是在每次点击时动态计算变量,然后可供视图和模板使用。我是否真的不在乎是否通过使用上下文处理器来完成。

提前致谢!

2 个答案:

答案 0 :(得分:2)

1.7和1.8之间的变化是引入了不同的模板后端,以便为例如Jinja2提供一流的支持。这意味着上下文处理器已经从顶级设置转变为与特定模板后端相关联的设置;例如,不推荐使用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))

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

答案 1 :(得分:1)

我发现了一个有帮助的解决方案,至少在我的情况下是这样。

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

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

长话短说,诀窍是使用中间件而不是上下文处理器,但是然后让上下文处理器继承来自中间件

考虑原始问题中的代码,与新代码进行比较:

Contextprocessor:

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,
        }

"全球"变量挂在一个名为 extravars 的任意命名字典中的请求对象上。请注意,虽然我们的"全球"变量条目仍然被称为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