当你需要向所有模板传递一个可以被全局使用的变量时。在编写Django视图函数时,我们一般会在视图函数中以Python字典(dict)形式向模板中传递需要被调用或使用的变量并指定渲染模板。通常情况下,我们向模板的传递的字典变量与模板是一一对应的关系。有时我们还需要向模板传递全局变量,即每个模板都需要使用到的变量(比如站点名称, 博客的最新文章列表)。
如果每个视图函数分别去查询数据库,然后向每个模板传递这些变量,不仅造成代码冗余,而且会造成对数据库的重复查询。一个更好的解决方案就是使用自定义的上下文处理器(Context Processors)给模板传递全局变量,一次查询全局使用,完美解决了这些问题。
你或许没有自定义过自己的全局上下文处理器(Context Processors),但你一定使用过Django内置的全局上下文处理器(Context Processors)。举个例子,虽然你没有向某个模板中传递过权限perms对象,你却可以在所有模板中随时调用它(如下所示)。同样可以在模板中全局使用的变量还有request和user对象。
为什么?因为Django的settings.py
里已经包含了django.template.context_processors.request
和django.contrib.auth.context_processors.auth
这两个全局上下文处理器。如果你把他们移除, 看看还能不能在模板中调用 user
和perms
?
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates')
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [ # 以下包含了4个默认的全局上下文处理器
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'myapp.custom_context_processors.xxx', # 自定义全局上下文处理器
],
},
},
]
Django一般包含了上述4个默认全局上下文处理器,它们作用如下所示:
另外Django还提供了几个全局上下文处理器:
自定义的全局上下文处理器本质上是个函数,使用它必须满足3个条件:
request
对象context_processors
里申明。我们通常会把自定义的上下文处理器函数放在单独命名的context_processors.py
里,这个python文件可以放在project目录下,也可以放在某个app的目录下。
接下来我们来看一个具体例子。我们需要向所有模板传递一个叫site_name
的全局变量以便在所有模板中直接使用 site_name
输出站点名称,我们可以在blog(应用名)的目录下新建context_processors.py
,新增如下代码:
# blog/context_processors.py
from django.conf import settings
def global_site_name(request):
return {'site_name': settings.SITE_NAME,}
然后可以在settings.py里声明:
'context_processors': [ # 以下包含了4个默认的全局上下文处理器
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'blog.context_processors.global_site_name', # 自定义全局上下文处理器
]
全局上下文处理器提供的变量优先级高于单个视图函数给单个模板传递的变量。这意味着全局上下文处理器提供的变量可能会覆盖你视图函数中自定义的本地变量,因此请注意避免本地变量名与全局上下文处理器提供的变量名称重复。这些变量名包括perms, user和debug等等。
如果你希望单个视图函数定义的变量名覆盖全局变量,请使用以下强制模式:
from django.template import RequestContext
high_priority_context = RequestContext(request)
high_priority_context.push({"my_name": "Adrian"})