为什么使用模板
视图函数的作用很明确,即生成请求的响应。但 Flask 视图函数的两个完全独立的作用(处理逻辑和页面)却被融在一起。
例如,用户在网站中注册了一个新账户。用户在表单中输入电子邮件地址和密码,然后点击提交按钮。服务器接收到包含用户输入数据的请求,然后 Flask 把请求分发到处理注册请求的视图函数。这个视图函数需要访问数据库,添加新用户,然后生成响应回送浏览器。这两个过程分别称为业务逻辑和表现逻辑。
Flask,使用模板将这两个逻辑分离。这样视图函数可以专注于处理业务逻辑,而表现逻辑则交给Flask的Jinjia2的模板引擎来处理。
jinjia2模板引擎
官方文档包括API,沙箱,模板设计者文档。
{% … %} for Statements
{{ … }} for Expressions to print to the template output
{# … #} for Comments not included in the template output
# … ## for Line Statements
建立user模板
<h1> Hello, {{ name }}! </h1>
渲染模板
Flask在程序文件夹的templates子文件夹中寻找模板。
渲染模板调用:render_template('user.html', var_key=var_value)
。 render_template 函数把 Jinja2 模板引擎集成到了程序中。第一个参数是模板的文件名。随后的参数都是键值对,表示模板中变量对应的真实值。左边的“var_key”表示参数名,就是模板中使用的占位符;右边的“var_value”是当前作用域中的变量,表示同名参数的值。
变量
{{ name }} 结构表示一个变量,它是一种特殊的占位符,告诉模板引擎这个位置的值从渲染模板时使用的数据中获取。
Jinja2 能识别所有类型的变量,甚至是一些复杂的类型,例如列表、字典和对象。
<p>a dictionary: {{ mydict['key'] }}.</p>
<p>a list: {{ mylist[3] }}.</p>
<p>a list, with a variable index: {{ mylist[myintvar] }}.</p>
<p>an object's method: {{ myobj.somemethod() }}.</p>
过滤器变量可以通过 过滤器 修改。过滤器与变量用管道符号( | )分割,并且也 可以用圆括号传递可选参数。多个过滤器可以链式调用,前一个过滤器的输出会被作为 后一个过滤器的输入。例如 {{ name|striptags|title }}
会移除 name 中的所有 HTML 标签并且改写 为标题样式的大小写格式。内置过滤器清单。
控制结构
Jinja2 提供了多种控制结构,可用来改变模板的渲染流程。
- 条件控制。
{ % if user % }
Hello, {{ user }}!
{ % else % }
Hello, Stranger!
{ % endif % }
- 循环控制
<ul>
{ % for comment in comments % }
<li>{{ comment }}</li>
{ % endfor % }
</ul>
- 宏
宏类似于 Python 代码中的函数。
<!--宏的定义-->
{ % macro render_comment(comment) % }
<li>{{ comment }}</li>
{ % endmacro % }
<!--宏的使用-->
<ul>
{ % for comment in comments % }
<!--宏调用-->
{{ render_comment(comment) }}
{ % endfor % }
</ul>
- 包含模板
需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免重复:
{ % include 'common.html' as common% }
{{common.XXXX}}
- 模板继承
它类似于 Python 代码中的类继承。
首先,定义基模板。如base.html
<html>
<head>
{ % block head % }
<title>{ % block title % }{ % endblock % } - My Application</title>
{ % endblock % }
</head>
<body>
{ % block body % }
{ % endblock % }
</body>
</html>
定义衍生模板。
{ % extends "base.html" % }
{ % block title % }Index{ % endblock % }
{ % block head % }
{{ super() }}
<style>
</style>
{ % endblock % }
{ % block body % }
<h1>Hello, World!</h1>
{ % endblock % }
{% extend %} 标签是这里的关键。它告诉模板引擎这个模板“继承”另一个模板。 当模板系统对这个模板求值时,首先定位父模板。 extends 标签应该是模板中的第一个 标签。它前面的所有东西都会按照普通情况打印出来,而且可能会导致一些困惑。
extends 指令声明这个模板衍生自 base.html。在 extends 指令之后,基模板中的 3 个块被重新定义,模板引擎会将其插入适当的位置。注意新定义的 head 块,在基模板中其内容不是空的,所以使用 super() 获取原来的内容,调用 super 来渲染父级块的内容。这会返回父级块的结果。
设计模板
使用Flask-Bootstrap
- 安装
pip install flask-bootstrap
- 初始化Flask-Bootstrap
from flask.ext.bootstrap import Bootstrap
# ...
bootstrap = Bootstrap(app)
初始化 Flask-Bootstrap 之后,就可以在程序中使用一个包含所有 Bootstrap 文件的基模板。这个模板利用Jinja2的模板继承机制,让程序扩展一个具有基本页面结构的基模板,其中就有用来引入 Bootstrap 的元素。官方教程
{% extends "bootstrap/base.html" %}<!--extends 指 令 从 Flask-Bootstrap中导入bootstrap/base.html,实现模板继承。 -->
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Flasky</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
</ul>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="container">
<div class="page-header">
<h1>Hello, {{ name }}!</h1>
</div>
</div>
{% endblock %}
定义错误页面
如果你在浏览器的地址栏中输入了不可用的路由,那么会显示一个状态码为 404 的错误页面。现在这个错误页面太简陋、平庸,而且样式和使用了 Bootstrap 的页面不一致。
- 定义错误处理函数。
与视图函数类似,只不过使用不同的修饰器@app.errorhandler(error_code)
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
- 编写对应的模板文件
链接
在模板中直接编写简单路由的 URL 链接不难,但对于包含可变部分的动态路由,在模板中构建正确的 URL 就很困难。而且,直接编写 URL 会对代码中定义的路由产生不必要的依赖关系。如果重新定义路由,模板中的链接可能会失效。Flask 提供了 url_for() 辅助函数,它可以使用程序 URL 映射中保存的信息生成 URL。
静态文件
用静态文件,例如 HTML代码中引用的图片、JavaScript 源码文件和 CSS。默认设置下,Flask 在程序根目录中名为 static 的子目录中寻找静态文件。如果需要,可在static 文件夹中使用子文件夹存放文件。
如索引图标的位置{{ url_for('static', filename = 'favicon.ico') }}
Flask-Moment本地化日期和时间
- 安装
pip install flask-moment
- 初始化
from flask.ext.moment import Moment
moment = Moment(app)
- 引入 moment.js 库
{ % block scripts % }
{{super()}}
{{moment.include_moment() }}
{ %endblock % }
- 加入一个 datetime 变量
from datetime import datetime
@app.route('/')
def index():
return render_template('index.html', current_time=datetime.utcnow())
- 渲染时间
<p>The local date and time is {{ moment(current_time).format('LLL') }}.</p>