模板系统基础

Django 中的模板系统是 Django 框架的重要组成部分。无论哪一个 Web 框架都需要提供动态生成 HTML 页面的方式,最常用的做法是使用模板。模板包含一些公共的 HTML 部分以及一些特殊的语法,该语法用于描述如何将数据动态插入 HTML 网页中。

Django 框架后端默认支持自生内置的一套模板系统 DTL (Django Template Language) 和著名的 Jinja2 模板系统。除此之外,还可以从第三方模块中选择其他模板系统。往往内置的模板系统以及 Jinja2 已经足够应对大多数场景了。

1. 什么是模板

模板在生活中也是非常常见的,比如我们上学时向老师请假,会有请假条的模板,只需在请假条模板中填写请假人以及请假原因就可以。HTML 的模板也是如此,只不过我们会有特殊的语法来完成一个模板,接下来会有对应的模板引擎来帮我们将传入的数据和模板中变量进行一一对应并最终形成一个完整的 HTML 文件。例如下面的一个最简单的模板文件:

<html>
<head></head>
<style type="text/css">
</style>
<body>
<h1>{{ title }}</h1>
</body>
</html>

这个简单的模板中,我们只有一个模板变量 title,用 {{}} 括起来。模板引擎会对这些变量进行数据替换,比如传入数据 {'title':'标题1'},那么模板引擎就会将 {{ title }} 直接替换成 标题1 字符串。不同的模板引擎支持不同的语法格式。在 Django 中应用最为广泛的模板引擎当属 Jinja2 了,当然它也是 Flask 框架默认使用的模板系统。

2. Python中的模板库

Python 中比较出名的模板库有 2 个,分别是 Mako 和 Jinja2。下面分别来说一说这两个模板库的用法和一些特点。

Mako 是一个高性能的 Python 模板库,它的语法和 API 借鉴了很多其他的模板库,如 Django、Jinja2 等等。它声称有比 Jinja2 更快的解析速度以及更多的语法支持。我们来简单过一遍 Mako 中模板语法并用一个案例进行测试:

  • 变量写法:${variable},在花括号中还可以执行一些 Python 的语法,如:${name.upper()} 会将 name 的值转换成大写;

  • 注释:单行注释用 ##,多行注释用 <%doc></%doc>

    ## 两个#号表示单行注释
    <%doc>
    多行注释
    </%doc>
    
  • if 条件语句:控制结构的语法都是以 % name 开头,以 % end 结尾;

    % if i == 1:
        ${i}
    % endif
    
  • for 循环:for 循环的语法是以 % for 开头,以 % endfor 结尾;

    % for a in [1, 2, 3, 4, 5]:
        % if a[0] == 1:
        its one
        % elif a[0] == 2:
        two
        % else:
        others
        % endif
    % endfor
    
  • Python 代码块:Mako 中嵌入 python 代码块时,使用标签 <%%>

    <%
    x=1000
    y='imooc'
    z=y.upper()
    %>
    ${x}
    ${y}
    ${z}
    

接下来,我们用编写简单的 Python 代码测试下前面用的这些模板语法,基于 Mako 模板引擎来将如下的模板文件渲染成完整的内容:

## coding=utf-8

<html>
<head></head>
<style type="text/css">
</style>
<body>
<h1>${title}</h1>
## 打印告警信息    
% if has_warn == 'warning':
      ${warn_msg}  
% endif    

## 打印动物列表 
<ul>    
% for animal in animals:
    <li>${animal}</li>
% endfor 
</ul>
    
## 测试 python 代码块
<%
x='imooc'
y=x.upper()
%>
<div>小写:${x}</div>
<div>大写:${y}</div>    
</body>
</html>

测试的 Python 代码如下:

# 先要安装 mako 模块: pip install mako
# 代码文件名为 test_mako.py
from mako.template import Template
from mako.lookup import TemplateLookup
html_path = '/root/spyinx'

lookup = TemplateLookup(
    directories=[html_path],
    output_encoding='utf-8',
    input_encoding='utf-8',
    default_filters=['decode.utf8'],
    encoding_errors='replace'
)
# 文件的全路径为/root/spyinx/index.html
tp = lookup.get_template("/index.html")
values = {
    'title': '测试模板转换',
    'has_warn': 'warning',
    'warn_msg': '这是一条告警信息',
    'animals': ['狮子', '老虎', '蛇']
}
print(str(tp.render(**values), encoding="utf-8"))

执行的结果如下:

(env-3.8.1) [root@server spyinx]# ls
index.html  test_mako.py
(env-3.8.1) [root@server spyinx]# python test_mako.py 

<html>
<head></head>
<style type="text/css">
</style>
<body>
<h1>测试模板转换</h1>
      这是一条告警信息  

<ul>    
    <li>狮子</li>
    <li>老虎</li>
    <li>蛇</li>
</ul>
    

<div>小写:imooc</div>
<div>大写:IMOOC</div>    
</body>
</html>

可以看到这里的模板文本已经被 Mako 引擎进行了更新,替换了其中的模板变量,去掉了注释部分,形成了最终的文本。

接下来,我们也简单介绍下 Jinja2 模板库,它的用法和 Mako 几乎是类似的,只不过支持的模板语法略有不同。我们会在后面详细介绍 Jinja2,这里简单介绍一些常用的语法并使用 Python 代码进行测试。

  • 变量写法:{{ variable }}

  • 注释:注释的语句是 {# 注释部分 #}

  • 条件语句:条件语句大多使用 if 语句,它也具有单分支,多分支等多种结构。使用时需要以 endif 关键字结尾,而且 if 、elif、else 和 endif 都需要用 {%%} 包裹;

    {% if spyinx.age < 18 %}
    spyinx is a minor
    {% elif spyinx.age > 50 %}
    spyinx is an old man
    {% else %}
    spyinx is an adult
    {% endif %}
    
  • 循环语句:循环语句大部分使用 for 语句,它的写法如下,和 mako 十分类似,使用 {%%} 包裹循环语句,还需要 {% endfor %} 结尾。

    {% for animal in animals %}
    {{ animal }}
    {% endfor %}
    

除了这些之外,还有过滤器、宏、模板的继承等等各种强大的功能,全方位满足各种场景。这些会留到后面详细介绍,下面来完成一个实验,使用 Jinja2 模板库完成对一个 Jinja2 模板文件的转换:

  • 准备好一个模板文件 index.html.j2,内容如下:

    <html>
    <head></head>
    <style type="text/css">
    </style>
    <body>
    <h1>{{ title }}</h1>
    {# 打印告警信息 #}
    {%- if has_warn == 'warning' -%}
    {{ warn_msg }}
    {%- endif -%}
    
    {# 打印动物列#}
    <ul>
    {%- for animal in animals %}
        <li>{{ animal }}</li>
    {%- endfor %}
    </ul>
    
    <div>首字母大写:{{ 'imooc' | capitalize  }}</div>
    <div>大写:{{ 'imooc' | upper }}</div>
    </body>
    </html>
    

    注意:这里使用 {%- 的写法是为了去掉类似 {% if|for|endif|endfor ... %} 这样的语句产生的空格。

  • 准备好测试的 python 代码,如下:

    """
    测试 jinja2 模块
    """
    from jinja2 import Environment, FileSystemLoader
    
    def render_template(path, file_name, vars):
        env = Environment(loader=FileSystemLoader('./'))   # 加载模板
        template = env.get_template(file_name)
        content = template.render(vars)
        print(content)
        
    
    if __name__ == '__main__':
        values = {
           'title': '测试模板转换',
           'has_warn': 'warning',
           'warn_msg': '这是一条告警信息',
           'animals': ['狮子', '老虎', '蛇']
        }
        render_template('./', 'index.html.j2', values)
    
  • 执行该 python 代码,我们可以得到和前面 Mako 模板库一样的结果:

    (env-3.8.1) [root@server spyinx]# python test_jinja2.py 
    <html>
    <head></head>
    <style type="text/css">
    </style>
    <body>
    <h1>测试模板转换</h1>
    这是一条告警信息
    <ul>
        <li>狮子</li>
        <li>老虎</li>
        <li>蛇</li>
    </ul>
        
    <div>首字母大写:Imooc</div>
    <div>大写:IMOOC</div>    
    </body>
    </html>
    

至此,我们对 Python 中的模板库有了初步的认识,接下来就来看看 Django 中的模板系统,包括如何配置和使用。

3. 小结

本小节中,我们讲解了 Django 中模板系统的相关基础知识,然后简单学习了 Python 中的 2 个流行的模板库 MakoJinja2 并进行了代码测试。接下来便是正式开始深入 Django 中的模板系统,包括模板语法、过滤器以及自定义过滤器等。