模板基本语法
{% if user %}
{{ user }}
{% else %}
hello!
<ul>
{% for index in indexs %}
<li> {{ index }} </li>
{% endfor %}
</ul>
一 变量
在模板中{{ variable }}结构表示变量,是一种特殊的占位符,告诉模板引擎这个位置的值,从渲染模板时使用的数据中获取;Jinja2除了能识别基本类型的变量,还能识别{};
视图:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/index")
def index():
data = {
"name": "test",
"size": 11,
"adict": {"title": "index"},
"alist": [1,2,3,4,5,6]
}
return render_template('index.html', **data)
# return render_template("index.html", name="tile")
if __name__ == '__main__':
app.run()
模板:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta http-equiv='Content-type' content='text/htm'>
</head>
<body>
<p>name---{{ name }}</p>
<p>size---{{ size }}</p>
<p>adict---title:{{ adict["title"] }}</p>
<p>adict---title:{{ adict.title }}</p>
<p>alist---{{ alist }}</p>
<p>alist---:alist[0]:{{ alist[0] }}</p>
<p>alist---:alist[2]:{{ alist.2 }}</p>
<p>alist---:alist[0]+alist[1]:{{ alist[0] + alist[1] }}</p>
<p>{{ "hello" + "flask" }}</p>
</body>
</html>
自定义错误页面:
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
二 过滤器
过滤器的本质就是函数。有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化、运算等等,这就用到了过滤器。 过滤器的使用方式为:变量名 | 过滤器。 过滤器名写在变量名后面,中间用 | 分隔。如:{{variable | capitalize}},这个过滤器的作用:把变量variable的值的首字母转换为大写,其他字母转换为小写。 其他常用过滤器如下:
2.1 字符串过滤器:
safe:禁用转义;
<p>{{ '<em>hello</em>' | safe }}</p>
capitalize:把变量值的首字母转成大写,其余字母转小写;
<p>{{ 'hello' | capitalize }}</p>
lower:把值转成小写;
<p>{{ 'HELLO' | lower }}</p>
upper:把值转成大写;
<p>{{ 'hello' | upper }}</p>
title:把值中的每个单词的首字母都转成大写;
<p>{{ 'hello' | title }}</p>
trim:把值的首尾空格去掉;
<p>{{ ' hello world ' | trim }}</p>
reverse:字符串反转;
<p>{{ 'olleh' | reverse }}</p>
format:格式化输出;
<p>{{ '%s is %d' | format('name',17) }}</p>
striptags:渲染之前把值中所有的HTML标签都删掉;
<p>{{ '<em>hello</em>' | striptags }}</p>
2.2 支持链式使用过滤器
<p>{{ “ hello world “ | trim | upper }}</p>
2.3 列表过滤器
first:取第一个元素
<p>{{ [1,2,3,4,5,6] | first }}</p>
last:取最后一个元素
<p>{{ [1,2,3,4,5,6] | last }}</p>
length:获取列表长度
<p>{{ [1,2,3,4,5,6] | length }}</p>
sum:列表求和
<p>{{ [1,2,3,4,5,6] | sum }}</p>
sort:列表排序
<p>{{ [6,2,3,1,5,4] | sort }}</p>
2.4 语句块过滤
{% filter upper %}
this is a Flask Jinja2 introduction
{% endfilter %}
2.5 自定义过滤器:
过滤器的本质是函数。当模板内置的过滤器不能满足需求,可以自定义过滤器。自定义过滤器有两种实现方式:一种是通过Flask应用对象的add_template_filter方法。还可以通过装饰器来实现自定义过滤器。
自定义的过滤器名称如果和内置的过滤器重名,会覆盖内置的过滤器。
实现方式一:通过调用应用程序实例的add_template_filter方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称。
def list_step_2(li):
"""自定义过滤器"""
return li[::2]
# 注册过滤器
app.add_template_filter(list_step_2, "li2")
实现方式二:用装饰器来实现自定义过滤器。装饰器传入的参数是自定义的过滤器名称。
@app.template_filter("li3")
def list_step_3(li):
"""自定义过滤器"""
return li[::3]
三 Web表单:
web表单是web应用程序的基本功能。
它是HTML页面中负责数据采集的部件。表单有三个部分组成:表单标签、表单域、表单按钮。表单允许用户输入数据,负责HTML页面数据采集,通过表单将用户输入的数据提交给服务器。
在Flask中,为了处理web表单,我们一般使用Flask-WTF扩展,它封装了WTForms,并且它有验证表单数据的功能。
WTForms支持的HTML标准字段
字段对象 | 说明 |
---|---|
StringField | 文本字段 |
TextAreaField | 多行文本字段 |
PasswordField | 密码文本字段 |
HiddenField | 隐藏文本字段 |
DateField | 文本字段,值为datetime.date格式 |
DateTimeField | 文本字段,值为datetime.datetime格式 |
IntegerField | 文本字段,值为整数 |
DecimalField | 文本字段,值为decimal.Decimal |
FloatField | 文本字段,值为浮点数 |
BooleanField | 复选框,值为True和False |
RadioField | 一组单选框 |
SelectField | 下拉列表 |
SelectMultipleField | 下拉列表,可选择多个值 |
FileField | 文本上传字段 |
SubmitField | 表单提交按钮 |
FormField | 把表单作为字段嵌入另一个表单 |
FieldList | 一组指定类型的字段 |
WTForms常用验证函数
验证函数 | 说明 |
---|---|
DataRequired | 确保字段中有数据 |
EqualTo | 比较两个字段的值,常用于比较两次密码输入 |
Length | 验证输入的字符串长度 |
NumberRange | 验证输入的值在数字范围内 |
URL | 验证URL |
AnyOf | 验证输入值在可选列表中 |
NoneOf | 验证输入值不在可选列表中 |
使用Flask-WTF需要配置参数SECRET_KEY。
CSRF_ENABLED是为了CSRF(跨站请求伪造)保护。 SECRET_KEY用来生成加密令牌,当CSRF激活的时候,该设置会根据设置的密匙生成加密令牌。
在HTML页面中直接写form表单:
#模板文件
<form method='post'>
<input type="text" name="username" placeholder='Username'>
<input type="password" name="password" placeholder='password'>
<input type="submit">
</form>
视图函数中获取表单数据:
from flask import Flask,render_template,request
@app.route('/login',methods=['GET','POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
print username,password
return render_template('login.html',method=request.method)
使用Flask-WTF实现表单。
pip install Flask-WTF
配置参数:
app.config['SECRET_KEY'] = '.........'
模板页面:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta http-equiv='Content-type' content='text/htm'>
</head>
<body>
<form method="post">
{{ reg_form.csrf_token }}
{{ reg_form.username.label }}
<p>{{ reg_form.username }}</p>
{{ reg_form.username.errors.0 }}
{{ reg_form.password.label }}
<p>{{ reg_form.password }}</p>
{{ reg_form.password.errors.0 }}
{{ reg_form.repassword.label }}
<p>{{ reg_form.repassword }}</p>
{{ reg_form.repassword.errors.0 }}
<br>
{{ reg_form.submit }}
</form>
</body>
</html>
视图函数:
from flask import Flask, render_template, url_for, redirect, session
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, EqualTo
app = Flask(__name__)
app.config["SECRET_KEY"] = "ada898u2q11joloadad02"
class RegiterForm(FlaskForm):
username = StringField(label="用户名", validators=[DataRequired("用户名不能为空")])
password = PasswordField(label="密码", validators=[DataRequired("密码不能为空")])
repassword = PasswordField(label="确认密码", validators=[DataRequired("确认密码不能为空"), EqualTo("password", "两次密码输入不一致")])
submit = SubmitField(label="提交")
@app.route("/register", methods=['GET', "POST"])
def register():
# 创建表单对象, 如果是post请求, 前端发送了数据,flask会把数据在构造form对象的数据,存在对象中
reg_form = RegiterForm()
# 判断form中的数据是否合法
if reg_form.validate_on_submit():
username = reg_form.username.data
password = reg_form.password.data
repassword = reg_form.repassword.data
session["username"] = username
print(username, password, repassword)
return redirect(url_for("index"))
return render_template('register.html', reg_form=reg_form)
@app.route("/index")
def index():
username = session.get("username")
return "hello %s " % username
if __name__ == '__main__':
app.run()
四 控制语句
4.1 if控制语句
@app.route('/user')
def user():
user = 'wusong'
return render_template('user.html',user=user)
<html>
<head>
{% if user %}
<title> hello {{user}} </title>
{% else %}
<title> welcome to flask </title>
{% endif %}
</head>
<body>
<h1>hello world</h1>
</body>
</html>
4.2 for循环语句
@app.route('/loop')
def loop():
fruit = ['apple','orange','pear','grape']
return render_template('loop.html',fruit=fruit)
<html>
<head>
{% if user %}
<title> hello {{user}} </title>
{% else %}
<title> welcome to flask </title>
{% endif %}
</head>
<body>
<h1>hello world</h1>
<ul>
{% for index in fruit %}
<li>{{ index }}</li>
{% endfor %}
</ul>
</body>
</html>
五 宏、继承、包含
类似于python中的函数,宏的作用就是在模板中重复利用代码,避免代码冗余。
Jinja2支持宏,还可以导入宏,需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免重复。
5.1 宏
定义宏
{% macro input() %}
<input type="text" name="username" value="">
{% endmacro %}
调用宏
{{ input() }}
定义带参数的宏
{% macro input1(name, value="", type="text") %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
调用宏,并传递参数
{{ input1(pwd, type="password") }}
把宏单独抽取出来,封装成html文件,其它模板中导入使用
文件名可以自定义macro_input.html
{% macro function() %}
<input type="text" name="username" placeholde="Username">
<input type="password" name="password" placeholde="Password">
<input type="submit">
{% endmacro %}
在其它模板文件中先导入,再调用
{% import 'macro_input.html' as func %}
{{ func.function() }}
5.2模板继承
模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。
{% block top %}.......{% endblock %}标签定义的内容,相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。
父模板:base.html
{% block top %}
顶部菜单
{% endblock top %}
{% block content %}
{% endblock content %}
{% block bottom %}
底部
{% endblock bottom %}
子模板:
{% extends 'base.html' %}
{% block content %}
需要填充的内容
{% endblock content %}
模板继承使用时注意点:
-
不支持多继承。
-
为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行。
-
不能在一个模板文件中定义多个相同名字的block标签。
-
当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好。
5.3包含(Include)
Jinja2模板中,除了宏和继承,还支持一种代码重用的功能,叫包含(Include)。它的功能是将另一个模板整个加载到当前模板中,并直接渲染。
示例:
include的使用
{% include 'hello.html' %}
包含在使用时,如果包含的模板文件不存在时,程序会抛出TemplateNotFound异常,可以加上ignore missing关键字。如果包含的模板文件不存在,会忽略这条include语句。
示例:
include的使用加上关键字ignore missing
{% include 'hello.html' ignore missing %}
-
宏、继承、包含:
-
宏(Macro)、继承(Block)、包含(include)均能实现代码的复用。
-
继承(Block)的本质是代码替换,一般用来实现多个页面中重复不变的区域。
-
宏(Macro)的功能类似函数,可以传入参数,需要定义、调用。
-
包含(include)是直接将目标模板文件整个渲染出来。
-
六 Flask中的特殊变量和方法
在Flask中,有一些特殊的变量和方法是可以在模板文件中直接访问的。
config 对象:
config 对象就是Flask的config对象,也就是 app.config 对象。
{{ config.SQLALCHEMY_DATABASE_URI }}
request 对象:
就是 Flask 中表示当前请求的 request 对象,request对象中保存了一次HTTP请求的一切信息。
request常用的属性如下:
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求的数据,并转换为字符串 | * |
form | 记录请求中的表单数据 | MultiDict |
args | 记录请求中的查询参数 | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的报文头 | EnvironHeaders |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件 | * |
{{ request.url }}
url_for 方法:
url_for() 会返回传入的路由函数对应的URL,所谓路由函数就是被 app.route() 路由装饰器装饰的函数。如果我们定义的路由函数是带有参数的,则可以将这些参数作为命名参数传入。
{{ url_for('index') }}
{{ url_for('post', post_id=1024) }}
get_flashed_messages方法:
返回之前在Flask中通过 flash() 传入的信息列表。把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages() 方法取出。
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
示例:
from flask import Flask, render_template, flash
app = Flask(__name__)
app.config["SECRET_KEY"] = "diaj29u912kjwsle01sda"
flag = True
@app.route("/index")
def index():
global flag
if flag:
flash("flash1")
flash("flash2")
flash("flash3")
flag = False
return render_template("index.html")
if __name__ == '__main__':
app.run()
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta http-equiv='Content-type' content='text/htm'>
</head>
<body>
<h1>闪现</h1>
{% for msg in get_flashed_messages() %}
{{ msg }}
{% endfor %}
</body>
</html>