在通常的 Web 程序中,访问一个 URL 地址,一般都会返回一个 HTML 页面,而我们的数据就是嵌套在这些 HTML 代码当中的,再辅以 JavaScript 和 CSS 等,就组成了一个完整的前端页面了。
当然,对于 Flask 来说,它所包含的 HTML 页面还会包含一些变量和逻辑运算等代码,这些组合到一起就是模板。执行这些变量替换和逻辑计算工作的过程被称为渲染,这个工作由 Flask 自带的模板渲染引擎——Jinja2 来完成。
按照默认的设置,Flask 会从程序实例所在模块同级目录的 templates 文件夹中寻找模板,所以我们可以在 app.py 文件的同级目录下创建一个 templates 文件夹,用来存储模板文件。
下面我们先来简单熟悉下 Flask 模板的用法
模板基本语法
我们先来看一段例子,下面是 HTML 代码中嵌套了 Jinja2 的控制语法
<h1>{{ username }}的 Web 网站</h1> {% if auth %} <p>{{ admin }}</p> {% else %} <p>陌生人</p> {% endif %} {# 大部分 Jinja 语句都需要声明关闭 #}
模板基本的三种定界符
{{ … }} 用来标记变量。比如在上例中就可以渲染出 username 所对应的具体数据
{% … %} 用来标记语句,比如 if 语句,for 语句等。例子中的 if…else… 语句就是简单的判断
{# … #} 用来写注释。
在 Jinja2 中允许我们使用大部分 Python 对象,比如字符串、列 表、字典、元组、整型、浮点型、布尔值。它支持基本的运算符号 (+、-、*、/等)、比较符号(比如==、!=等)、逻辑符号(and、 or、not和括号)以及in、is、None和布尔值(True、False)。
渲染模板
我们一般使用 Flask 提供的渲染函数 render_template() 来渲染模板
from flask import render_template @app.route('/test') def test(): name = 'admin' return render_template('test.html', user=name)
render_template 函数第一个参数为模板文件名,一般会在 templates 目录下寻找。还可以以关键字参数的形式传入变量到模板,这样在模板中就可以使用 user 这个变量了,其变量值为 ‘admin’。
下面我们就把上一节中在视图函数中书写的 HTML 代码整理到对应的模板中
整理模板
对于 needLogin1 页面,我们原来的视图函数是
@app.route('/needlogin1/') def needLogin1(): if 'loginID' in session: return '<h1>Hello, needLogin1!</h1>' else: return """ <h1>Login</h1><a href="%s">Go To Login</a> """ % url_for('login', next=request.url)
我们可以分别在 templates 创建两个文件,hello.html 和 needlogin.html
对于 hello.html 文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>My Web</title> </head> <body> <h1>Hello, {{ user }}!</h1> </body> </html>
对于 needlogin.html 文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>My Web</title> </head> <body> <h1>Login</h1><a href="{{ url_for('login', next=request.url) }}">Go To Login</a> </body> </html>
接着我们再改写视图函数 needLogin1
@app.route('/needlogin1/') def needLogin1(): if 'loginID' in session: user = 'needLogin1' return render_template('hello.html', user=user) else: return render_template('needlogin.html')
这部分代码可以检出代码3a来查看
链接
在上面 needlogin.html 文件中你应该发现了,对于 href 属性,我们使用了 url_for() 这个函数来获取 URL 链接,这是和前面讲解的视图函数中 url_for 非常相似的。
url_for() 函数最简单的用法是以视图函数名作为参数,返回对应的 URL。当然还是像例子中展示的那样,传递额外的参数到查询字符串中 url_for('login', next=request.url)。
变量与结构控制
在前面我们简单实践了 Flask 模板的变量与结构控制的用法,下面我们来详细说明下它们的功能
变量
在上面的例子中,我们使用了 {{ user }} 表示一个变量,它是一种特殊的占位符,告诉模板引擎这个位置的值需要从渲染模板时使用的数据中获取。
Jinja2 可以识别很多复杂的变量类型,比如列表、字典,对象等。
<p>这是字典: {{ mydict['key'] }}.</p> <p>这是列表: {{ mylist[3] }}.</p> <p>可变索引的列表: {{ mylist[myindex] }}.</p> <p>调用方法: {{ myobj.somemethod() }}.</p>
还可以使用一些过滤器,来修改变量,从而达到快速处理数据的效果。过滤器名添加在变量名之后,中间使用竖线分隔。
{{ name|title }} # 把变量name标题化
一些常用的过滤器
过滤器 | 说明 |
safe | 渲染值时不转义 |
capitalize | 首字母大写,其余字母小写 |
lower | 转换成小写 |
upper | 转换成大写 |
trim | 去掉首尾空格 |
escape | 转义 HTML 文本 |
default | 设置默认值 |
这里我们着重说一下 safe 过滤器,默认情况下,Jinja2 会转义所有 HTML 标签,比如如果传<h1>你好</h1>
,会被渲染成”<hi>你好</h1>“,此时浏览器就会把 <h1>
标签当作时一个普通字符来展示。而当我们需要把变量按照 HTML 代码来展示时,就可以使用 safe 过滤器
{{ "<h1>你好</h1>"|safe }}
控制结构
我们常用的控制结构有两种,即 for 循环和 if 判断。下面是这两种控制结构的例子
# if 判断 {% if user %} Hello, {{ user }}! {% else %} Hello, Stranger! {% endif %} # for 循环 {% for user in users %} <li>{{ user }}</li> {% endfor %}
模板继承
模板的继承类似于 Python 中的继承,即我们可以把页面上的一些通用原始编写到基模板中,然后其他子模版只需要编写对应页面特殊的代码即可,其他比如导航栏,页脚等内容可以放到基模板中编写。
先来编写基模板,base.html
<html> <head> {% block head %} <title>{% block title %}{% endblock %} - My Web</title> {% endblock %} </head> <body> {% block body %} {% endblock %} </body> </html>
在基模板中,我们用 block 来标记需要替换的部分,在上面的例子中我们定义了 head、title 和 body 块,那么子模版就可以任意修改这三块内容了
编写子模板,我们创建一个 user.html 模板
{% extends "base.html" %} {% block title %}User{% endblock %} {% block head %} {{ super() }} <style> </style> {% endblock %} {% block body %} <h1>Hello, World!</h1> {% endblock %}
使用 extends 来引入基模板,之后再重新定义3个 block 块。这里需要注意 super 的使用,如果在子模板中没有 super,那么子模板会覆盖基模板中的内容,如果添加了 super,就是追加内容,即对应的 block 块中会包含基模板的内容。
集成Bootstrap
Bootstrap 是 Twitter 开发一个开源框架,他有许多可用的界面组件,可以帮助我们快速的搭建前端页面。当然要集成 Bootstrap 框架,我们就需要对模板进行相关的改动,而这些完全可用交给插件 Flask-Bootstrap 来完成。
pip install flask-bootstrap # 安装插件
在 Flask app 中初始化 Flask-Bootstrap
from flask_bootstrap import Bootstrap ... bootstrap = Bootstrap(app)
初始化 Flask-Bootstrap 之后,就可以在程序中使用一个包含所有 Bootstrap 文件的基模板。这个模板利用 Jinja2 的模板继承机制,让程序扩展一个具有基本页面结构的基模板。
完善程序
下面我们就把前面写的模板通过 bootstrap 来优化下,首先是基模板,包含通用的导航栏
{% extends "bootstrap/base.html" %} {% block title %}My Web{% endblock %}{% block navbar %} <div class="navbar navbar-default navbar-static-top" role="navigation"> <div class="container"> <div class="navbar-header"> <button class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon icon-bar"></span> <span class="icon icon-bar"></span> <span class="icon icon-bar"></span> </button> <a href="#" class="navbar-brand">HelloFlask</a> </div> <div class="collapse navbar-collapse"> <ul class="nav navbar-nav navbar-right"> <li><a href="#home" class="smoothScroll">首页</a></li> <li><a href="#intro" class="smoothScroll">网站简介</a></li> <li><a href="#team" class="smoothScroll">风格</a></li> <li><a href="#portfolio" class="smoothScroll">作品</a></li> <li><a href="#contact" class="smoothScroll">联系我们</a></li> </ul> </div> </div> </div> {% endblock %}{% block content %} <div class="container"> {% block page_content %}{% endblock %} </div> {% endblock %}
接下来我们在 hello.html 模板上继承该基模板,并查看效果
{% extends "base.html" %} {% block title %}My Web - Index{% endblock %}{% block page_content %} <div class="page-header"> <h1>Hello, {{ user }}!</h1> </div> {% endblock %}
刷新页面,可以看到现在的页面如下
添加 Favicon
Favicon 是网站的特殊标记,可以展示在收藏夹、浏览器标签中的图标。现在我们就为我们的网站添加一个 Favicon。
首先我们在 templates 文件夹的同级目录上创建一个 static 文件夹,然后在使用一些在线工具制作 favicon.ico 文件,并放置到该目录下
在线转换工具有很多,可以使用这个
最后我们在 HTML 页面中声明 Favicon 的路径,在 base.html 模板中的 标签中添加一个
<head> {% block head%} {{ super() }} <meta charset="utf-8"> <title> {% block title %}My Web{% endblock %} </title> <link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico')}}"> {% endblock %} </head>
消息闪现
Flask 还提供了一个非常有用的函数 flash(),它可以在页面上闪现需要展示给用户的消息。它的工作原理就是把相关的信息存储在浏览器的 session 中,然后我们在模板中使用全局函数 get_flashed_messages() 获取消息并将其显示出来。
下面我们对登陆成功的用户闪现消息,先在登陆视图函数中添加 flash
@app.route('/login/') def login(): session['loginID'] = 'admin' target = request.args.get('next') flash(u"你登陆成功了!") if check_next(target): return redirect(target) return redirect(url_for('hello'))
然后再在 hello.html 模板中渲染 flash 消息
{% extends "base.html" %}{% block title %}My Web - Index{% endblock %}{% block page_content %}{% for message in get_flashed_messages() %} <div class="alert alert-warning"> <button type="button" class="close" data-dismiss="alert">×</button> {{ message }} </div> {% endfor %} <div class="page-header"> <h1>Hello, {{ user }}!</h1> </div> {% endblock %}
最终的效果
这部分的代码,可以检出代码库中的 3b
总结
本节我们学习了 Flask 中模板的使用,这对于我们快速完成 Web 应用开发是非常方便的。