当我在编写一个高度自定义的后台系统时,我希望将网站的一些信息存储在数据库中,用户可以通过页面修改就能生效,就像下图配置一样
通常页面中用到的变量需要在view中回传,但站点名称网站中所有页面都需要用到,难道每一个view都需要回传一遍吗?这让我想到了页面中经常用到的获取用户名方法request.user.username
,request
变量并没有在每个view中回传,但所有页面都可以调用,他是如何实现的?下文将为你详细介绍,了解之后可以通过编写自定义的全局变量,轻松解决以上问题
在日常开发Django的过程中,如果你有用到默认的template,那么通常会通过request.user.username
来获取登陆用户的用户名,你有没有想过这个request
是哪来的?即便是后端view
里不返回这边变量依然可以使用,怎么会如此神奇
这要从Django默认配置文件settings.py
里的TEMPLATES
配置说起,默认的TEMPLATES配置如下
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
其中BACKENDS
指定了Djanog默认使用的模版引擎,默认的是Django自己开发的DjangoTemplates
,当然你也可以替换成功能更为强大的jinja2
,DIRS
指定了项目中模板文件的位置,APP_DIRS
配置是否开启在已安装的app下查找template,OPTIONS
则指定template的后端设置
之所以可以在模板中使用request
的奥秘便隐藏在context_processors
下,这个配置指定了Django渲染时执行的Python路径列表。当模板在渲染时,会执行context_processors
列表中的所有函数,并将结果与上下文的context进行合并,也就是说模板接收到的参数除了render
返回的context
外,还有以上这个列表执行返回的结果
django.template.context_processors.request
函数返回的字典中包含了request
def request(request):
return {'request': request}
所以如下这样一个view,即便是返回结果中没有返回request
,我们依然可以在setting.html
模版中使用request.user
def setting(request):
return render(request, 'setting.html', {})
同样的我们可以直接在模板中使用perms.opscoffee.select_user
来判断用户是否拥有相应的权限,而不需要在view中返回perms
,这也是因为django.contrib.auth.context_processors.auth
返回了perms
def auth(request):
"""
Return context variables required by apps that use Django's authentication
system.
If there is no 'user' attribute in the request, use AnonymousUser (from
django.contrib.auth).
"""
if hasattr(request, 'user'):
user = request.user
else:
from django.contrib.auth.models import AnonymousUser
user = AnonymousUser()
return {
'user': user,
'perms': PermWrapper(user),
}
类似request
和perms
这些在所有模板中都可以调用的变量,可以看作是django的全局变量了
上边我们知道了Django是如何定义全局变量requests
的,那么我们只需照虎画猫写个类似的函数并加入context_processors
就可以了,实现步骤如下
先在名为commons
的APP下创建个文件context_processors.py
,编写如下的函数,返回site
作为全局变量
from commons.models import Setting
def site(request):
site_name = Setting.objects.filter(key='site_name')
site_title = Setting.objects.filter(key='site_title')
site_name = site_name.first().value if site_name else ''
site_title = site_title.first().value if site_title else ''
return {'site': {'site_name': site_name, 'site_title': site_title}}
然后在TEMPLATES
的context_processors
写上对应的路径
TEMPLATES = [
{
...
'OPTIONS': {
'context_processors': [
...
'commons.context_processors.site', # 添加这一行信息
],
},
},
]
最后就可以在模板中通过site.site_title
来获取站点title,以及通过site.site_name
获取站点名称
<head>
<title>{{ site.site_title }}</title>
</head>
<body>
<div class="wrapper">
<header class="main-header">
<a href="/" class="logo">
{{ site.site_name }}
</a>
</header>
</div>
</body>
至此,问题顺利解决