Flask 的 jinja2 模板

还记的 “第一个 Flask 程序”这一小节中我们直接在 Python 源程序中直接返回一段 HTML 字符串,就可以在浏览器中看到效果,但是如果我们的 HTML 特别复杂呢?我们还能使用这样的形式吗?

答案是不行的,如果你的 HTML 特别复杂仍然使用这样的方法会造成你的后端程序非常的混乱,Python 代码和 HTML 代码混杂在一起,程序的可阅读行非常差。

本节课我们将会使用一个叫做 Jinja2 的东西来解决上面提到的问题。

Tips:本节课所有的代码已经上传到了 Github,可以点击这里进行下载。

1. 模板简介

1.1 简介

浏览器访问网站时,服务端通常会返回一个包含各类信息的 html 页面。因为网页是动态的,页面中的某些信息需要根据不同的情况来进行调整,比如对登录和未登录用户显示不同的信息,所以页面需要在用户访问时根据程序逻辑动态生成。

包含变量和运算逻辑的 html 或其他格式的文本叫做模板,执行这些变量替换和逻辑计算工作的过程被称为渲染,在 Flask 中,这个工作由模板渲染引擎——jinja2 来完成。

1.2 一个具体的例子

以下是一个 jinja2 的模板,它对登录和未登录用户显示不同的信息:

<html>
{% if login %}
  <p>你好,{{name}}</p>
{% else %}
  <a href='/login'>登录</a>
{% endif %}
</html>

如果用户已经登录:变量 login 为真、变量 name 为 tom,模板被渲染成如下的 html 文件:

<html>
  <p>你好,tom</p>
</html>

如果用户没有登录:变量 login 为假,模板被渲染成如下的 html 文件:

<html>
  <a href='/login'>登录</a>
</html>

2. 在 Flask 中使用模板

本节通过一个具体的例子讲解如何在 Flask 中使用 jinja2 模板。

2.1 目录结构

程序包含有两个源文件: app.py 和 index.html,目录结构如下:

图片描述

app.py 是 Flask 程序,将渲染后的模板返回给浏览器;templates 是存放模板的目录,它和 app.py 位于相同的目录;templates/index.html 是模板文件。

2.2 编写 app.py

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html', name = 'tom', age = 10)

app.run(debug = True)

在第 1 行,从模块 flask 中导入函数 render_template,该函数将 jinja2 模板渲染为 html;在第 3 行,编写路径 / 的处理函数 index(),调用 render_template,对模板 templates/index.html 进行渲染。

render_template 包含有 2 个命名参数:name 和 age,模板引擎将模板 templates/index.html 中的变量进行替换。

2.3 编写模板文件 templates/index.html

<html>
<body>
  <h2>My name is {{ name }}, I am {{ age }} years old</h2>
</body>
</html>

模板文件中的 {{ name }} 和 {{ age }} 被称为 jinja2 变量。render_template 包含有两个命名参数: name 和 age,{{ name }} 被替换为 name,{{ age }} 被替换为 age,最后渲染的 html 如下:

<html>
<body>
  <h2>My name is tom, I am 10 years old</h2>
</body>
</html>

在浏览器中显示如下:

图片描述

3. 分界符

jinja2 模板文件混合 html 语法与 jinja2 语法,使用分界符区分 html 语法与 jinja2 语法。有 5 种常见的分界符:

  • {{ 变量 }},将变量放置在 {{ 和 }} 之间;
  • {% 语句 %},将语句放置在 {% 和 %} 之间;
  • # 语句,将语句放置在 # 之后;
  • {# 注释 #},将注释放置在 {# 和 #} 之间;
  • ## 注释,将注释放置在 # 之后。

以下模板文件包含了所有常见的分界符:

<!DOCTYPE html>
<html lang="en">
<body>
  {{ variable }}    

  <ul>
  {% for item in seq %}
    <li>{{ item }}</li>
  {% endfor %}
  </ul>

  <ul>
  # for item in seq
    <li>{{ item }}</li>
  # endfor
  </ul>

  {# this is comment #}
  ## this is comment
</body>
</html>

4. 变量

1. 语法

jinja2 模板中,使用 {{ 和 }} 包围的标识符称为变量,模板渲染会将其替换为 Python 中的变量,语法如下:

 {{ 变量 }}

2. jinja2 模板

定义演示变量功能的模板:

<html>
{{ string }}

<ul>
  <li> {{ list[0] }}
  <li> {{ list[0] }}
  <li> {{ list[1] }}
</ul>    

<ul>
  <li> {{ dict['name'] }}
  <li> {{ dict['age'] }}
</ul>    
</html>

包含有 3 种类型的变量:字符串、列表、字典,它们会被替换为同名的 Python 变量。

3. jinja2 的模板输入

string = 'www.imooc.com'
list = ['www', 'imooc', 'com']
dict = {'name': 'zhangsan', 'age': 12}

4. 渲染后的 HTML

<html>
www.imooc.com

<ul>
  <li> www
  <li> www
  <li> imooc
</ul>    

<ul>
  <li> zhangsan
  <li> 12
</ul> 

5. for 语句

1. 语法

jinja2 模板中,使用 {% 和 %} 包围的语法块称为语句,jinja2 支持类似于 Python 的 for 循环语句,语法如下:

{% for item in iterable %}
{% endfor %}

或者

# for item in iterable
# endfor

以上两者是等价的。

2. jinja2 模板

定义一个演示 for 循环语句功能的模板:

<h1>Members</h1>
<ul>
{% for user in users %}
  <li>{{ user }}</li>
{% endfor %}
</ul>

在第 3 行,定义 for 循环语句,遍历列表 users;在第 4 行,在循环体中使用 {{ user }} 引用当前正在被遍历的元素。

3. jinja2 的模板输入

users = ['tom', 'jerry', 'mike']

4. 渲染后的 HTML

<h1>Members</h1>
<ul>
  <li>tom</li>
  <li>jerry</li>
  <li>mike</li>
</ul>

6. if 语句

1. 语法

jinja2 模板中,使用 {% 和 %} 包围的语法块称为语句,jinja2 支持类似于 Python 的 if-else 判断语句,语法如下:

{% if cond %}
{% else %}
{% endif %}

jinja2 支持类似于 Python 的 if-elif 判断语句,语法如下:

{% if cond %}
{% elif cond %}
{% endif %}

2. jinja2 模板

定义一个演示 if 语句功能的模板:

<html>
{% if a %}
  <p>a is True</p>
{% else %}
  <p>a is False</p>
{% endif %}

{% if b %}
  <p>b is True</p>
{% elif c %}
  <p>b is False, and c is True</p>
{% endif %}
</html>

在模板中根据变量 a、b、c 的取值生成不同的内容。

3. jinja2 的模板输入

a = False
b = False
c = True

4. 渲染后的 html


<html>
  <p>a is False</p>
  <p>b is False, and c is True</p>
</html>

7. 测试

1. 语法

jinja2 提供的 tests 可以用来在语句里对变量或表达式进行测试,语法如下:

{% variable is test %}
test 名称 功能
defined 变量是否已经定义
boolean 变量的类型是否是 boolean
integer 变量的类型是否是 integer
float 变量的类型是否是 float
string 变量是否是 string
mapping 变量的类型是否是字典
sequence 变量的类型是否是序列
even 变量是否是偶数
odd 变量是否是奇数
lower 变量是否是小写
upper 变量是否是大写

2. jinja2 模板

定义一个演示 test 功能的模板:

<html>
{% if number is odd %}
  <p> {{ number }} is odd
{% else %}
  <p> {{ number }} is even
{% endif %}

{% if string is lower %}
  <p> {{ string }} is lower
{% else %}
  <p> {{ string }} is upper
{% endif %}
</html>

在第 2 行,number is odd 测试 number 是否为奇数;在第 8 行,string is lower 测试 string 是否为小写。

3. jinja2 的模板输入

number = 404
string = 'HELLO'

4. 渲染后的 html

<html>
  <p> 404 is even
  <p> HELLO is upper
</html>

8. 过滤器

1. 语法

jinja2 过滤器的是一个函数,语法如下:

{{ variable | filter }}

执行函数调用 filter(varialbe),把函数返回值作为这个代码块的值。

在词条 “jinja2 模板过滤器的使用” 中,详细讲解过滤器,本节仅仅给出一个简单例子。

2. jinja2 模板

<html>
{{ string | upper }}
</html>

3. jinja2 的模板输入

string = 'hello'

4. 渲染后的 html

<html>
HELLO
</html>

9. 小结

本节讲解 Flask 的默认模板语言 jinja2,通过具体的例子讲解了 jinja2 中的变量、if 语句、for 语句等概念,使用思维导图概括如下:

图片描述