《老鸟python 系列》视频上线了,全网稀缺资源,涵盖python人工智能教程,爬虫教程,web教程,数据分析教程以及界面库和服务器教程,以及各个方向的主流实用项目,手把手带你从零开始进阶高手之路!点击 链接 查看详情

模板加载

阅读:184262906    分享到

为了减少模板加载调用过程及模板本身的冗余代码,Django 提供了一种使用方便且功能强大的 API,用于从磁盘中加载模板。

要使用此模板加载API,首先你必须将模板的保存位置告诉框架。设置的保存文件就是我们前一章节讲述 ROOT_URLCONF 配置的时候提到的 settings.py

如果你是一步步跟随我们学习过来的,马上打开你的 settings.py 配置文件,找到 TEMPLATES 这项设置吧。它的默认设置是一个空列表(list),列表元素值是字典类型。

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        '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',
            ],
        },
    },
]

该设置告诉 Django 的模板加载机制在哪里查找模板。TEMPLATES 中有一个字典元素,我们选择一个目录用于存放模板并将其添加到该元素中的 'DIRS' 键对应的值(list)中,为了简单描述代码,我们下面只展示要修改的 'DIRD' 字段项:

'DIRS': ['E:/djproject/mysite/templates']

下面是一些注意事项:

你可以任意指定想要的目录,只要运行 Web 服务器的用户可以读取该目录的子目录和模板文件。如果实在想不出合适的位置来放置模板,我们建议在 Django 项目中创建一个 templates 目录(也就是说,如果你一直都按本书的范例操作的话,在第二章创建的 mysite 目录中)。

以上的路径写法,也可以使用 Windows 路径表达方式,比如: 'E:\\djproject\\mysitemysite\\templates',而上面代码的路径写法,我们使用了 Unix 路径书写方式(E:/djproject/mysite/templates)。你不用担心,Django 会把这种 Unix 路径风格转为和 Windows 操作系统对应的路径表达方式。但是,如果我们的项目运行在 类 Unix(Linux,mac)操作系统环境下,则需要写成 Unix 路径风格。所以,为了代码的通用性,我们都写成 Unix 风格的路径书写方式。

最省事的方式是使用绝对路径(即从文件系统根目录开始的目录路径)。如果想要更灵活一点并减少一些负面干扰,可利用 Django 配置文件就是 Python 代码这一点来动态构建 TEMPLATES 的内容,例如:
import os

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',
            ],
        },
    },
]
在 settings.py 文件中,你可以找到 'BASE_DIR' 这个变量对应的值为 os.path.dirname(os.path.dirname(os.path.abspath(__file__)))'),我们来仔细分析一下该值,该值里面有个神奇的 Python 内部变量 __file__,该变量被自动设置为代码所在的 Python 模块文件名。os.path.dirname(os.path.abspath(__file__)) 将会获取自身所在的文件,即settings.py 所在的目录,然后我们在使用 os.path.dirname 获取 mysite 文件夹所在目录(项目根目录),最后由 os.path.join 这个方法将项目根目录与 templates 目录进行连接。在这里我们面对的是动态语言 python 代码,我需要提醒你的是,不要在你的设置文件里写入错误的代码,这很重要。如果你在这里引入了语法错误,或运行错误,你的 Django-powered 站点将很可能就要被崩溃掉。

完成 TEMPLATES 设置后,下一步就是修改视图代码和编写 urls.py 配置。下面我们先看一下 urls.py 配置,这个配置很简单,后面讲解中,我可能不在鳌述关于 urls.py 配置的问题,你应该可以根据视图文件写好这个配置。

from django.contrib import admin
from django.urls import path
from django.urls import re_path
from mysite.views import current_datetime

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^time/$', current_datetime),
]

下面,我们仔细分析一下视图文件里面的代码,在此我们使用 Django 模板加载功能而不是对模板路径硬编码,对视图函数 current_datetime,进行如下修改:

from django.template.loader import get_template
from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    t = get_template('current_datetime.html')
    context = {'current_date': now}
    html = t.render(context)
    return HttpResponse(html)

此范例中,我们使用了函数 django.template.loader.get_template(),而不是手动从文件系统加载模板。该 get_template() 函数以模板名称为参数,在文件系统中找出模块的位置,打开文件并返回一个编译好的 Template 对象。

在这个例子里,我们选择的模板文件是 current_datetime.html,但这个与 .html 后缀没有直接的联系。你可以选择任意后缀的任意文件,只要是符合逻辑的都行。甚至选择没有后缀的文件也不会有问题。

要确定某个模板文件在你的系统里的位置,get_template() 方法会自动为你连接已经设置的 TEMPLATES 目录和你传入该法的模板名称参数。比如,你的 TEMPLATES 目录设置为 'E:/djproject/mysite/templates',上面的 get_template()调用就会为你找到 E:/djproject/mysite/templates/current_datetime.html 这样一个位置。

如果 get_template() 找不到给定名称的模板,将会引发一个 TemplateDoesNotExist 异常。要了解究竟会发生什么,让我们按照第三章内容,在 Django 项目目录中运行 python manage.py runserver 命令,再次启动 Django 开发服务器。接着,告诉你的浏览器,使其定位到指定页面以激活 current_datetime 视图(如 http://127.0.0.1:8000/time/ )。假设你的 DEBUG 项设置为 True,而你有没有建立 current_datetime.html 这个模板文件,你会看到 Django 的错误提示网页,告诉你发生了 TemplateDoesNotExist 错误。

该页面与我们在第三章解释过的错误页面相似,只不过多了一块调试信息区:模板加载器事后检查区。该区域显示 Django 要加载哪个模板、每次尝试出错的原因(如:文件不存在等)。当你尝试调试模板加载错误时,这些信息会非常有帮助。

接下来,在模板目录中创建包括以下模板代码 current_datetime.html 文件:

<html><body>It is now {{ current_date }}.</body></html>

在网页浏览器中刷新该页,你将会看到完整解析后的页面。

render_to_response()

我们已经告诉你如何载入一个模板文件,然后用 render 函数 渲染它,最后返回这个处理好的 HttpResponse 对象给用户。我们已经优化了方案,使用 get_template() 方法代替繁杂的用代码来处理模板及其路径的工作。但这仍然需要一定量的时间来敲出这些简化的代码。这是一个普遍存在的重复苦力劳动。Django 为此提供了一个捷径,让你一次性地载入某个模板文件,渲染它,然后将此作为 HttpResponse 返回。

该捷径就是位于 django.shortcuts 模块中名为 render_to_response() 的函数。大多数情况下,你会使用该对象,除非你的老板以代码行数来衡量你的工作。

下面就是使用 render_to_response() 重新编写过的 current_datetime 范例。

from django.shortcuts import render_to_response
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    return render_to_response('current_datetime.html', {'current_date': now})

大变样了! 让我们逐句看看代码发生的变化:

  • 我们不再需要导入 get_templateTemplateContextHttpResponse。相反,我们导入 django.shortcuts.render_to_responseimport datetime 继续保留。

  • current_datetime 函数中,我们仍然进行 now 计算,但模板加载、上下文创建、模板解析和 HttpResponse 创建工作均在对 render_to_response() 的调用中完成了。由于 render_to_response() 返回 HttpResponse 对象,因此我们仅需在视图中 return 该值。

render_to_response() 的第一个参数必须是要使用的模板名称。如果要给定第二个参数,那么该参数必须是为该模板创建 Context 时所使用的字典。如果不提供第二个参数,render_to_response() 使用一个空字典。

locals() 技巧

思考一下我们对 current_datetime 的最后一次赋值:

def current_datetime(request):
    now = datetime.datetime.now()
    return render_to_response('current_datetime.html', {'current_date': now})

很多时候,就像在这个范例中那样,你发现自己一直在计算某个变量,保存结果到变量中(比如前面代码中的 now ),然后将这些变量发送给模板。尤其喜欢偷懒的程序员应该注意到了,不断地为临时变量 临时模板命名有那么一点点多余。不仅多余,而且需要额外的输入。

如果你是个喜欢偷懒的程序员并想让代码看起来更加简明,可以利用 Python 的内建函数 locals()。它返回的字典对所有局部变量的名称与值进行映射。因此,前面的视图可以重写成下面这个样子:

def current_datetime(request):
    current_date = datetime.datetime.now()
    return render_to_response('current_datetime.html', locals())

在此,我们没有像之前那样手工指定 context 字典,而是传入了 locals() 的值,它囊括了函数执行到该时间点时所定义的一切变量。因此,我们将 now 变量重命名为 currentdate,因为那才是模板所预期的变量名称。在本例中,locals() 并没有带来多大的改进,但是如果有多个模板变量要界定而你又想偷懒,这种技术可以减少一些键盘输入。

使用 locals() 时要注意是它将包括 所有 的局部变量,它们可能比你想让模板访问的要多。在前例中,locals() 还包含了 request。对此如何取舍取决你的应用程序。

get_template()中使用子目录

把所有的模板都存放在一个目录下可能会让事情变得难以掌控。你可能会考虑把模板存放在你模板目录的子目录中,这非常好。事实上,我们推荐这样做;一些 Django 的高级特性(后面我们会讲到)的缺省约定就是期望使用这种模板布局。

把模板存放于模板目录的子目录中是件很轻松的事情。只需在调用 get_template() 时,把子目录名和一条斜杠添加到模板名称之前,如:

t = get_template('dateapp/current_datetime.html')

由于 render_to_response() 只是对 get_template() 的简单封装,你可以对 render_to_response() 的第一个参数做相同处理。

return render_to_response('dateapp/current_datetime.html', {'current_date': now})

对子目录树的深度没有限制,你想要多少层都可以。只要你喜欢,用多少层的子目录都无所谓。

include 模板标签

在讲解了模板加载机制之后,我们再介绍一个利用该机制的内建模板标签:{% include %}。该标签允许在(模板中)包含其它的模板的内容。标签的参数是所要包含的模板名称,可以是一个变量,也可以是用单/双引号硬编码的字符串。 每当在多个模板中出现相同的代码时,就应该考虑是否要使用 {% include %} 来减少重复。

下面这两个例子都包含了 nav.html 模板。这两个例子是等价的,它们证明单/双引号都是允许的。

{% include 'nav.html' %}
{% include "nav.html" %}

下面的例子包含了 includes/nav.html 模板的内容:

{% include 'includes/nav.html' %}

下面的例子包含了以变量 template_name 的值为名称的模板内容:

{% include template_name %}

和在 get_template() 中一样,对模板的文件名进行判断时会在所调取的模板名称之前加上来自 TEMPLATES 的模板目录。

所包含的模板执行时的 context 和包含它们的模板是一样的。举例说,考虑下面两个模板文件:

# mypage.html

<html>
<body>
{% include "includes/nav.html" %}
<h1>{{ title }}</h1>
</body>
</html>

# includes/nav.html

<div id="nav">
    You are in: {{ current_section }}
</div>

如果你用一个包含 current_section的上下文去渲染 mypage.html这个模板文件,这个变量将存在于它所包含(include)的模板里,就像你想象的那样。

如果{% include %}标签指定的模板没找到,Django将会在下面两个处理方法中选择一个:

  • 如果 DEBUG 设置为 True ,你将会在 Django 错误信息页面看到 TemplateDoesNotExist 异常。

  • 如果 DEBUG 设置为 False ,该标签不会引发错误信息,在标签位置不显示任何东西。


如果以上内容对您有帮助,请老板用微信扫一下赞赏码,赞赏后加微信号 birdpython 领取免费视频。


登录后评论