概述
Flask中我们通常使用Jinjia2模板语言来实现复杂的页面渲染,Jinja2 是一个现代的,设计者友好的,仿照 Django 模板的 Python 模板语言。 它速度快,被广泛使用,并且提供了可选的沙箱模板执行环境保证安全,它的特性有:
- 沙箱中执行
- 强大的 HTML 自动转义系统保护系统免受 XSS
- 模板继承
- 及时编译最优的 python 代码
- 可选提前编译模板的时间
- 易于调试。异常的行数直接指向模板中的对应行。
- 可配置的语法
一个典型的Jinja2模板
一个典型的Jinja2模板如下所示,这里面展示了如何定义区块(block),如何迭代数据,如何显示数据。
<title>{{title}}</title>
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
将上述代码放置在当前目录的templates目录下面,然后在当前目录编写如下代码:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/hello')
def hello():
users = [{"username":"users1","url":"/users/user1"},
{"username":"users2","url":"/users/user2"}]
return render_template("hello.html",title="User List",users=users)
if __name__ == '__main__':
app.run(debug=True)
打开浏览器,输入http://localhost:5000/hello,我们看到代码运行的效果如下:
在Flask中使用模板
Flask可以在视图中或错误处理函数中使用模板渲染的功能。在Flask中渲染Jinja2模板非常简单,上面就是一个典型的例子。使用from flask import render_template导入Flask自带的模板渲染功能,在视图函数中使用如下格式的语句得到模板渲染后的文本变量。
render_template("模板文件",参数1,参数2...)
默认情况下,Flask会在包含app = Flask(_name_)的源代码目录下的templates目录中查找“模板文件”,我们的教程中,如果没有特别说明,app在视图文件中生成,如果视图函数中出现render_template(“index.html”)语句,Flask会在视图所在目录下的templates目录中查找index.html。我们也可以在templates目录中按照自己的业务逻辑设置子目录,这样的话渲染函数看起来可能是这样的:render_template(“user/list.html”)。
如果你不想把模板放在默认的templates目录下,可以在生成app对象的时候指定一个参数使用自己的模板目录:
app = Flask(__name__,template_folder='tpl')
基本的模板用法
显示
显示各类Python支持的数据是模板的最基本用法。
字符串/数字
字符串和数字是最基本的数据类型,可以使用一对双大括号显示此类数据,例如
<title>{{title}}</title>
列表
列表类型的数据我们需使用一个for循环对数据进行迭代,例如:
{% for user in users %}
<li>{{ user }}</a></li>
{% endfor %}
上面的代码通过for语句和endfor构成了一个代码块,代码块中的所有内容(包括HTML和变量值)都会根据迭代的数量多次生成。
字典
字典可以使用点操作符或者下标访问的方式取得值。例如:
Username:{{ user['name'] }}
Email: {{ user.email }}
如果元素的key没有在字典中找到,Jinja2通常不会抛出异常,这点在调试的时候需要格外注意,在没有显示数据时你要自己弄清楚是缺少key,还是该key对应的值是空。
对象
严格的说,上面介绍的几种数据类型在Python中也是以对象的形式存在的,所以本质上说模板中使用常用的数据类型和使用自定义对象是一回事。假设我们向模板传递了一个名称是obj的对象,它有一个属性name,有一个方法say,在模板中使用类似下面的代码分别去访问其属性和方法。
<h5>{{obj.name}}:</h5>
<p>{{obj.say()}}</p>
循环
循环通常用来显示一个可迭代的对象,例如上文中使用循环展示一个列表中的全部内容。常见的数据类型中除了列表,字典也可以使用for循环进行迭代。生成一个列表或者具有多行数据的表格是循环常见的用法,比如:
<h1>Members</h1>
<ul>
{% for user in users %}
<li>{{ user.username }}</li>
{% endfor %}
</ul>
可以使用sort过滤器为迭代排序(默认的排序规则是字母顺序或数值顺序),针对一些简单的数据类型(如字符串或数字构成的列表、简单的key:value型的字典)我们直接这样去使用它:
{% for item in [4,2,1,3]|sort() %}
{{item}}
{% endfor %}
可以使用reverse=True指定按照倒序排列。
{% for k,v in {"key3":3,"key2":2,"key1":1}.items()|sort(reverse=True) %}
{{k}}:{{v}}
{% endfor %}
针对一些复杂的对象,我们可以使用attribute=’?’指定按照某个属性排序,比较常见的是由字典构成的列表,下面的例子按照字典中的email进行排序:
{% for item in [{"username":"wang","email":"2@x.com"},
{"username":"chen","email":"6@x.com"},
{"username":"guo","email":"1@x.com"}]|sort(attribute='email') %}
{{item.username}}
{% endfor %}
Jinjia2为循环内置了一个非常有用的特殊对象loop,可以在循环内部的代码块中调用。例如下面的代码可以在表格中显示一个从1开始的序号:
<h1>Members</h1>
<table>
<th>
<td>Sequnce</td>
<td>Username</td>
</th>
{% for user in users %}
<tr>
<td>loop.index</td>
<td>{{ user.username }}</td>
</tr>
{% endfor %}
</table>
loop对象的详细说明见下表。
属性值 | 描述 |
---|---|
loop.index | 当前迭代的索引,从1开始算 |
loop.index0 | 当前迭代的索引,从0开始算 |
loop.revindex | 相对于序列末尾的索引,从1开始算 |
loop.revindex0 | 相对于序列末尾的索引,从0开始算 |
loop.first | 相当于 loop.index == 1. |
loop.last | 相当于 loop.index == len(seq) - 1 |
loop.length | 序列的长度. |
loop.cycle | 可以接受两个字符串参数,如果当前循环索引是偶数,则显示第一个字符串,是奇数则显示第二个字符串。 |
loop.cycle常被在表格中用来用不同的背景色区分相邻的行。例如:
{% for row in rows %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
{% endfor %}
需要注意的是,Jinja的循环不支持break和continue标记。你可以对需要迭代的sequence使用过滤器来达到与break和continue相同的目的。
下面的例子中,如果user.hidden属性为true的则continue
{% for user in users if not user.hidden %}
<li>{{ user.username|e }}</li>
{% endfor %}
Jinja的for语句有一个和python相同的用法,那就是else:当被迭代的对象元素个数为0时显示else中的内容,如下例:
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% else %}
<li><em>no users found</em></li>
{% endif %}
</ul>
判断
同大多数据编程语言一样,Jinja2的判断使用if语句实现,比较常见的用法是判断一个变量是否已定义,是否非空,是否为true。if的用法比较简单,举个例子向大家说明一下:
{% if users %}
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}
和python一样,也可以使用elif和else
{% if kenny.sick %}
Kenny is sick.
{% elif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}