目录:
【Flask学习笔记:一】开发环境部署
【Flask学习笔记:二】Flask 入门基础知识
【Flask学习笔记:三】Flask 中的 request、response
【Flask学习笔记:四】Flask 应用和请求上下文
【Flask学习笔记:五】Jinja2 模板引擎
【Flask学习笔记:六】Flask 蓝图
【Flask学习笔记:七】Flask - WTF 处理表单
【Flask学习笔记:八】Flask 中的 cookie、session
【Flask学习笔记:九】Flask-SQLAlchemy
在 Flask 中通常使用 Jinja2 模板引擎来实现复杂的页面渲染。Jinja2 被认为是灵活、快速和安全的模板引擎技术,被广泛使用。Jinja2 的设计思想来源于 Django 模板引擎,它功能强大、速度快,并且提供了可选的沙箱模板执行环境安全保证机制,具有沙箱中执行、强大的 HTML 转义系统、模板继承等诸多优点。
文件结构:
/static
/style.css
/templates
/login.html
/success.html
/demo.py
demo.py 文件代码如下:
from flask import Flask, render_template, request, url_for
from werkzeug.utils import redirect
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/')
def index():
return redirect(url_for('login'))
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
username = request.form.get('username')
password = request.form.get('password')
return render_template('success.html', username=username, password=password)
if __name__ == '__main__':
app.run(port=8080)
login.html 代码如下:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
head>
<body>
<div class="container">
<span>登录span>
{# 添加的注释 #}
<form action="{{ url_for('login') }}" method="post">
<p>username: <input type="text" name="username" />p>
<p>password: <input type="password" name="password" />p>
<input type="submit" value="Submit" />
form>
div>
body>
html>
success.html 代码如下:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
登陆成功,欢迎 {{ username }}
body>
html>
Flask 通过 render_template() 函数来实现模板的渲染。要使用 Jinja 2 模板引擎,需要使用 from flask import render_template 命令导入 render_template 函数。在视图函数的 return 方法中, render_template() 函数第一个参数是指定模板文件的名称,比如这里的 login.html 和 success.html。
render_template() 函数的第二个参数为可选项,可以为空。比如 login() 视图函数中方法为 GET 请求的 render_template(‘login.html’)。当然也可以传递第二个参数,比如例子中的 render_template(‘success.html’, username=username, password=password),一般是以键值对方式传递的。
在模板中接受变量值,需要把变量放在 {{}} 中,比如 {{ username }}。模板中如果要写注释,格式为{# #},比如这里的 {# 添加的注释 #}。如果视图函数中有多个变量值都需要传递给模板,可以使用 **locals() 方法,改写之前的 login() 函数:
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
username = request.form.get('username')
password = request.form.get('password')
return render_template('success.html', **locals())
在 render_template() 函数中,如果要给模板传递传递全部的本地变量,可以使用**local() 方法,此时,在模块中可以直接使用{{ username }}来直接使用变量。
在 Jinja 2 模板引擎中也可以使用 if 和 for 循环控制语句,控制模板的渲染。模板引擎中,if 和 for 语句应该放到 {% %}中。
控制语句 if:
if 语句的使用方式:
{% if 条件1 %}
do something
{% elif 条件2 %}
do something
{% else %}
do something
{% endif %}
控制语句 for:
for 循环语句是 Python 中的一个循环控制语句,任何有序的序列对象内的元素都可以遍历,比如字符串、列表、元组等可迭代对象。模板中 for 循环的使用方式:
{% for 目标 in 对象 %}
<p>目标</p>
{% endfor %}
实例:
from flask import Flask, render_template
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/')
def index():
dict_res = {
'a': 'a',
'b': 'b',
'c': 'c'
}
list_res = [1, 2, 3]
return render_template('index.html', **locals())
if __name__ == '__main__':
app.run(port=8080)
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
{% for key, value in dict_res.items() %}
<p>{{ key }} : {{ value }}p>
{% endfor %}
{% for value in list_res %}
<p>{{ value }}p>
{% endfor %}
body>
html>
输出结果为:
a : a
b : b
c : c
1
2
3
Jinja 2 中 for 循环内特殊的变量有:
变量 | 描述 |
---|---|
loop.index | 当前循环迭代的次数(从 1 开始) |
loop.index0 | 当前循环迭代的次数(从 0 开始) |
loop.revindex | 到循环结束需要迭代的次数(从 1 开始) |
loop.revindex0 | 到循环结束需要迭代的次数(从 0 开始) |
loop.first | 如果是第一次迭代,为 True |
loop.last | 如果是最后一次迭代,为 True |
loop.length | 序列中的项目数 |
loop.cycle | 在一串序列间期取值的辅助函数,见下面的例子 |
在 for 循环中,可以使用特殊的 loop.cycle 辅助函数,伴随循环在一个字符串/变量列表中周期取值:
{% for row in rows %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
{% endfor %}
注意: 与 Python 中不同,不可以使用 continue 和 break 表达式来控制循环的执行。
如果你在浏览器的地址栏中输入了无效的路由,会看到一个状态码为 404 的错误页面。这样的页面显的比较简陋,Flask 允许应用使用模板自定义错误页面。最常见的错误代码有两个: 404,客户端请求未知页面或路由时显示;500,应用有未处理的异常时显示。用 app.errorhandler 装饰器为这两个错误提供自定义的处理函数。
from flask import render_template
@app.errorhandler(404)
def page_not_found(error):
return render_template('page_not_found.html'), 404
@app.errorhandler(500)
def internal_server_error(error):
return render_template('internal_server_error.html'), 500
常见过滤器:
过滤器本质上是一个转换函数,有时候我们不仅需要输出变量的值,还需要把某个变量的值修改后再显示出来,而在模板中不能直接调用 Python 中的某些方法, 这就需要用到过滤器,过滤器与变量用管道符号( | )分割。
这里列举一个常用的过滤器:
更多内置过滤器介绍详见 文档
自定义过滤器:
当内置的过滤器满足不了我们的需求时,我们其实可以写出属于自己的过滤器。通过调用应用程序实例的 add_template_filter 方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称:
from flask import Flask, render_template
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/')
def index():
list_res = [1, 2, 3]
return render_template('index.html', **locals())
def do_index_class(index): # 实现自定义过滤器,对第一个数加9
if index == 1:
return index + 9
else:
return index
app.add_template_filter(do_index_class, 'index_class')
if __name__ == '__main__':
app.run(port=8080)
使用自定义过滤器:
{% for value in list_res %}
<p>{{ value | index_class }}</p>
{% endfor %}
打印结果:
10
2
3
Jinja 2 中的宏功能有些类似于传统程序语言中的函数,它跟 Python 中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来作为一个变量。
5.1.宏的定义
宏有声名和调用两个部分。定义宏要加 macro,宏定义结束要加 endmacro 标志。举个例子:
{% macro input(name, type='text', value='') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}" />
{% endmacro %}
<p>用户名:{{ input('username') }}p>
<p>密码:{{ input('password', type='password') }}p>
<p>登录:{{ input('submit', type='submit', value='登录') }}p>
在这个例子中,宏的名称就是 input, 它有 3 个参数,分别是name、type 和 value,后两个参数有默认值。还可以在宏中加入 size 和 placeholder 等属性。
在宏内部,你可以访问三个特殊的变量:
名称 | 说明 |
---|---|
varargs | 如果有多于宏接受的参数个数的位置参数被传入,它们会作为列表的值保存在 varargs变量上 |
kwargs | 同 varargs ,但只针对关键字参数。所有未使用的关键字参数会存储在 这个特殊变量中 |
caller | 如果宏通过 call 标签调用,调用者会作为可调用的宏被存储在这个变量中 |
宏也可以暴露某些内部细节。下面的宏对象属性是可用的:
名称 | 说明 |
---|---|
name | 宏的名称 |
arguments | 一个宏接受的参数名的元组 |
defaults | 默认值的元组 |
catch_kwargs | 如果宏接受额外的关键字参数(也就是访问特殊的 kwargs 变量),则为 true |
catch_varargs | 如果宏接受额外的位置参数(也就是访问特殊的 varargs 变量),则为 true |
caller | 如果宏访问特殊的 caller 变量且由 call 标签调用,则为 true |
例:
{% macro input(name, type, value) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}" />
<br /> varargs : {{ varargs }}
<br /> kwargs : {{ kwargs }}
<br /> name : {{ input.name }}
<br /> arguments : {{ input.arguments }}
<br /> defaults : {{ input.defaults }}
<br /> caller : {{ input.caller }}
<br /> catch_kwargs : {{ input.catch_kwargs }}
<br /> catch_varargs : {{ input.catch_varargs }}
{% endmacro %}
<p>用户名:{{ input('username','text','text','varargs1', 'varargs2', kwargs1='12345') }}p>
结果为:
在某些情况下,需要把一个宏传递到另一个宏。为此,可以使用特殊的 call 块。 下面的例子展示了如何让宏利用调用功能:
{% macro render_dialog(title, class='dialog') -%}
<div class="{{ class }}">
<h2>{{ title }}h2>
<div class="contents">
{{ caller() }}
div>
div>
{%- endmacro %}
{% call render_dialog('Hello World') %}
This is a simple dialog rendered by using a macro and
a call block.
{% endcall %}
5.2.宏的导入
一个宏可以被不同的模板使用,所以建议将其声明在一个单独的模板文件中。需要使用时导入即可,而导入的方法类似于 Python 中的 import 。可以使用两种方式导入:
{% from 'forms.html' import input as input_field %} # 第一种方式
{% import 'forms.html' as forms %} # 第二种方式
单独定义宏的文件forms.html:
{% macro input(name, value='', type='text') -%}
<input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">
{%- endmacro %}
index.html:
{% import 'forms.html' as forms %}
<p>用户名:{{ forms.input('username') }}p>
<p>密码:{{ forms.input('password', type='password') }}p>
<p>登录:{{ forms.input('submit', type='submit', value='登录') }}p>
注意: 如果一个宏的名称以下划线开始,表示它是私有的,不能被导入
5.3. include 的使用
include 语句可以把一个模板引入到另一个模板中,类似于把一个模板的代码复制到另一个模板的指定位置。
例:
header.html代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div>
header
div>
body>
html>
footer.html代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div>
footer
div>
body>
html>
index.html代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
head>
<body>
{% include "header.html" %}
<div>
网页内容
div>
{% include "footer.html" %}
body>
html>
显示效果:
注意: 使用 include 标签时是在 templates 目录中找寻相应的文件,不要使用相对路径。
set 和 with 语句都可以在 Jinja 2 中定义变量并赋值。 set 定义的变量在整个模板范围内都有效, with 关键字在定义变量并赋值的同时,限制了 with 定义变量的作用范围,在这个作用域中的变量对外部是不可见的。
{% with %}
{% set foo = 42 %}
{{ foo }} foo is 42 here
{% endwith %}
foo 不可访问
Jinja 2 中最强大的部分就是模板继承,一个页面中都有标题、内容显示、底部等几个部分。我们可以将其中相同的部分提取出来,做为一个基础模板 base.html,其它网页就可以通过继承基础模板来显示相同的内容。模板的继承语法为:
{% extends '模板名称' %}
定义一个基模板 base.html:
<html lang="en">
<head>
{% block head %}
<title>{% block title %}super title{% endblock title %}title>
{% endblock head %}
head>
<body>
<div>{% block content %}super content{% endblock content %}div>
<div>
{% block footer %}
super footer
{% endblock footer %}
div>
body>
html>
子模版 index.html 继承基模板:
{% extends 'base.html' %}
{% block title %}
{{ super() }}
subclass title
{% endblock title %}
{% block content %}
subclass content
{% endblock content %}
{% block footer %}
super footer
{{ self.content() }}
{% endblock footer %}
结果显示为:
默认情况下,子模版如果实现了父模板定义的 block,那么子模版 block 中的代码就会覆盖父模板中的代码。如果想要在子模版中仍然保持父模板中的代码,那么可以使用 {{ super() }} 来实现,如例子中的 title 块,如果想要在一个 block 中调用其他 block 中的代码,可以通过 {{ self.block名称() }} 实现,如例子中的 footer 块复用 content 块代码。
此外,Jinja 2 还允许你在块的结束标签中加入名称来改善代码的可读性,endblock 后面的名称一定与块名称相同。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% for i in range(1,10) %}
{% for j in range(1,i+1) %}
{{ j }} * {{ i }} = {{ i*j }} |
{% endfor %}
<br />
{% endfor %}
</body>
</html>