默认情况下,Flask 在程序文件夹中的 templates 子文件夹中寻找模板。在下一个 hello.py 版本中,要把前面定义的模板保存在 templates 文件夹中,并分别命名为 index.html 和 user.html。
index.thm的代码如下,不传参数
欢迎来到王者荣耀
你是不是要成为一个高手
user.html中传递一个参数
欢迎来到王者荣耀
你是不是要成为一个高手,你的名字是{{name}}
在hello.py中,
from flask import render_template
@app.route('/')
def index():
return render_template('index.html')
@app.route('/user/') #尖括号中的动态名字
def user(name):
return render_template('user.html',name=name)
1、变量
在模板中使用的 {{ name }}
结构表示一个变量,它是一种特殊的占位符,告诉模
板引擎这个位置的值从渲染模板时使用的数据中获取。
Jinja2 能识别所有类型的变量,甚至是一些复杂的类型,例如列表、字典和对象。在模板 中使用变量的一些示例如下:
A value from a dictionary: {{ mydict['key'] }}.
A value from a list: {{ mylist[3] }}.
A value from a list, with a variable index: {{ mylist[myintvar] }}.
A value from an object's method: {{ myobj.somemethod() }}.
可以使用过滤器修改变量,过滤器名添加在变量名之后,中间使用竖线分隔。例如,下述 模板以首字母大写形式显示变量 name 的值:
Hello, {{ name|capitalize }}
下面是常用的过滤器
更多的fliter官方文档
2、Jinja2的控制结构
if - else 的写法
{% if name %}
Hello,{{name}}
{% else %}
Hello ,你没名字一看就是菜啊!!!
{% endif %}
for 循环
{% for title in ["黄金4","黄金3","黄金2"] %}
{{title}}
{% endfor %}
Jinja2 还支持宏。宏类似于 Python 代码中的函数。例如:
{% macro render_comment(comment) %}
{{ comment }}
{% endmacro %}
{% for comment in comments %}
{{ render_comment(comment) }}
{% endfor %}
为了重复使用宏,我们可以将其保存在单独的文件中,然后在需要使用的模板中导入:
{% import 'macros.html' as macros %}
{% for comment in comments %}
{{ macros.render_comment(comment) }}
{% endfor %}
需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免 重复:
{% include 'common.html' %}
另一种重复使用代码的强大方式是模板继承,它类似于 Python 代码中的类继承。首先,创
建一个名为 base.html 的基模板:
{% block head %}
{% block title %}{% endblock %} - MyApplication
{% endblock %}
{%block body %}
我是来自基类
{%endblock %}
新建一个son.html 继承base.html
{% extends "base.html" %}
{% block title %} INDEX {% endblock %}
{% block head %}
{{super()}}
{% endblock %}
{% block body %}
{{super()}}
我也是醉了,这是啥基础自基础模板
{% endblock %}
extends 指令声明这个模板衍生自 base.html。在 extends 指令之后,基模板中的 3 个块被 重新定义,模板引擎会将其插入适当的位置。注意新定义的 head 块,在基模板中其内容不 是空的,所以使用 super() 获取原来的内容。
3、自定义错误页面的方式
自定义错误页面
@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
4、链接
任何具有多个路由的程序都需要可以连接不同页面的链接,例如导航条。
在模板中直接编写简单路由的 URL 链接不难,但对于包含可变部分的动态路由,在模板 中构建正确的 URL 就很困难。而且,直接编写 URL 会对代码中定义的路由产生不必要的 依赖关系。如果重新定义路由,模板中的链接可能会失效。
为了避免这些问题,Flask 提供了 url_for() 辅助函数,它可以使用程序 URL 映射中保存 的信息生成 URL。
url_for() 函数最简单的用法是以视图函数名(或者 app.add_url_route() 定义路由时使用 的端点名)作为参数,返回对应的 URL。例如,在当前版本的 hello.py程序中调用 url_ for('index')得到的结果是/。调用url_for('index', _external=True)返回的则是绝对地 址,在这个示例中是 http://localhost:5000/。
生成连接程序内不同路由的链接时,使用相对地址就足够了。如果要生成在 浏览器之外使用的链接,则必须使用绝对地址,例如在电子邮件中发送的 链接。
使用 url_for() 生成动态地址时,将动态部分作为关键字参数传入。例如,url_for ('user', name='john', _external=True) 的返回结果是 http://localhost:5000/user/john。
传入 url_for() 的关键字参数不仅限于动态路由中的参数。函数能将任何额外参数添加到 查询字符串中。例如,url_for('index', page=2) 的返回结果是 /?page=2。
5、静态文件
Web 程序不是仅由 Python 代码和模板组成。大多数程序还会使用静态文件,例如 HTML
代码中引用的图片、JavaScript 源码文件和 CSS。
例如,调用 url_for('static', filename='css/styles.css', _external=True) 得 到 的 结 果 是 http:// localhost:5000/static/css/styles.css。
默认设置下,Flask 在程序根目录中名为 static 的子目录中寻找静态文件。如果需要,可在 static 文件夹中使用子文件夹存放文件。服务器收到前面那个 URL 后,会生成一个响应, 包含文件系统中 static/css/styles.css 文件的内容。
6、使用Flask-Moment本地化日期和时间
有一个使用 JavaScript 开发的优秀客户端开源代码库,名为 moment.js(http://momentjs. com/),它可以在浏览器中渲染日期和时间。Flask-Moment 是一个 Flask 程序扩展,能把 moment.js 集成到 Jinja2 模板中。Flask-Moment 可以使用 pip 安装:
pip3 install flask-moment
这个扩展的初始化方法如示例 3-11 所示。 示例 3-11 hello.py:初始化 Flask-Moment
from flask.ext.moment import Moment moment = Moment(app)
7、使用Flask-Bootstrap集成Twitter Bootstrap
安装
pip3 install flask-bootstrap
初始化 Flask-Bootstrap
from flask_bootstrap import Bootstrap
bootstrap = Bootstrap(app)
新建一个home.html模板,继承bootstrap的base.html模板
{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
Hello, {{ name }}!
{% endblock %}
{% endblock %}
{% block content %}
Flask-Bootstrap 的 base.html 模板还定义了很多其他块,都可在衍生模板中使用。表 3-2 列
上图中很多块都是 Flask-Bootstrap 自用的,如果直接重定义可能会导致一些问题。例 如,Bootstrap 所需的文件在 styles 和 scripts 块中声明。如果程序需要向已经有内容的块 中添加新内容,必须使用 Jinja2 提供的 super() 函数。例如,如果要在衍生模板中添加新 的 JavaScript 文件,需要这么定义 scripts 块:
{% block scripts %}
{{ super() }}
{% endblock %}
7、web表单
尽管 Flask 的请求对象提供的信息足够用于处理 Web 表单,但有些任务很单调,而且要重 复操作。比如,生成表单的 HTML 代码和验证提交的表单数据。
Flask-WTF(http://pythonhosted.org/Flask-WTF/)扩展可以把处理 Web 表单的过程变成一 种愉悦的体验。这个扩展对独立的 WTForms(http://wtforms.simplecodes.com)包进行了包 装,方便集成到 Flask 程序中。
Flask-WTF 及其依赖可使用 pip 安装:
pip3 install flask-wtf
默认情况下,Flask-WTF能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,
CSRF)的攻击。恶意网站把请求发送到被攻击者已登录的其他网站时就会引发 CSRF 攻击。 为了实现 CSRF 保护,Flask-WTF 需要程序设置一个密钥。Flask-WTF 使用这个密钥生成
加密令牌,再用令牌验证请求中表单数据的真伪。设置密钥的方法设置 Flask-WTF
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
33
app.config 字典可用来存储框架、扩展和程序本身的配置变量。使用标准的字典句法就能 把配置值添加到 app.config 对象中。这个对象还提供了一些方法,可以从文件或环境中导 入配置值。
SECRET_KEY 配置变量是通用密钥,可在 Flask 和多个第三方扩展中使用。如其名所示,加 密的强度取决于变量值的机密程度。不同的程序要使用不同的密钥,而且要保证其他人不 知道你所用的字符串。
表单类:
使用 Flask-WTF 时,每个 Web 表单都由一个继承自 Form 的类表示。这个类定义表单中的 一组字段,每个字段都用对象表示。字段对象可附属一个或多个验证函数。验证函数用来 验证用户提交的输入值是否符合要求。
下面是一个简单的 Web 表单,包含一个文本字段和一个提交按钮。
from flask_wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import Required
class NameForm(Form):
name = StringField('What is your name?', validators=[Required()])
submit = SubmitField('Submit')
这个表单中的字段都定义为类变量,类变量的值是相应字段类型的对象。在这个示例中, NameForm 表单中有一个名为 name 的文本字段和一个名为 submit 的提交按钮。StringField 类表示属性为 type="text" 的 元素。SubmitField 类表示属性为 type="submit" 的 元素。字段构造函数的第一个参数是把表单渲染成 HTML 时使用的标号。
StringField 构造函数中的可选参数 validators 指定一个由验证函数组成的列表,在接受 用户提交的数据之前验证数据。验证函数 Required() 确保提交的字段不为空。
WTForms 支持的 HTML 标准字段如下图
WTForms 内建的验证函数
把表单类渲染为HTML
例通过参数 form 传入模板,在模板中可以生成一个简单的表单,如下所示:
即便能指定 HTML 属性,但按照这种方式渲染表单的工作量还是很大,所以在条件允许的 情况下最好能使用 Bootstrap 中的表单样式。Flask-Bootstrap 提供了一个非常高端的辅助函 数,可以使用 Bootstrap 中预先定义好的表单样式渲染整个 Flask-WTF 表单,而这些操作 只需一次调用即可完成。使用 Flask-Bootstrap,上述表单可使用下面的方式渲染:
{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form) }}
Flash消息
请求完成后,有时需要让用户知道状态发生了变化。这里可以使用确认消息、警告或者错 误提醒。一个典型例子是,用户提交了有一项错误的登录表单后,服务器发回的响应重新 渲染了登录表单,并在表单上面显示一个消息,提示用户用户名或密码错误。
这种功能是 Flask 的核心特性。
@app.route('/',methods = ['GET','POST'])
def index():
name = None
form = NameForm()
if form.validate_on_submit():
old_name = session.get('name')
new_name = form.name.data
session['name'] = new_name
if old_name is not None and old_name != new_name:
flash('你似乎改变了你的名字')
return redirect(url_for('index'))
return render_template('testForm.html', form=form, name=session.get('name'))
在这个示例中,每次提交的名字都会和存储在用户会话中的名字进行比较,而会话中存储 的名字是前一次在这个表单中提交的数据。如果两个名字不一样,就会调用 flash() 函数, 在发给客户端的下一个响应中显示一个消息。
获取get_flashed_messages()
来获取消息
在html中显示消息
{% for message in get_flashed_messages() %}
{{message}}
{% endfor %}