Flask_Jinja2模板(九)
在前面的示例中,视图函数的主要作用是生成请求的响应,这是最简单的请求。实际上,视图函数有两个作用:处理业务逻辑和返回响应内容。在大型应用中,把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本。本节学到的模板,它的作用即是承担视图函数的另一个作用,即返回响应内容。 模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体值需要从使用的数据中获取。使用真实值替换变量,再返回最终得到的字符串,这个过程称为“渲染”。Flask使用Jinja2这个模板引擎来渲染模板。Jinja2能识别所有类型的变量,包括{}。 Jinja2模板引擎,Flask提供的render_template函数封装了该模板引擎,render_template函数的第一个参数是要渲染的模板的文件名,后面的参数可以是键值对,也可以是**kwargs,均能将变量对应的真实值传递给模板。。
我们先来认识下模板的基本语法:
{% if user %}
{{ user }}
{% else %}
hello!
<ul>
{% for index in indexs %}
<li> {{ index }} </li>
{% endfor %}
</ul>
一、变量
Jinja2 模版中的变量代码块可以是任意 Python 类型或者对象,只要它能够被 Python 的 str() 方法转换为一个字符串就可以。
视图代码
from flask import Flask, render_template app = Flask(__name__) @app.route("/")
def index():
"""
"index.html"表示要渲染的模板
两种传递参数值的方式:
1、键值对
2、不定长字段参数**kwargs
"""
data = {
"name2": "李四",
"age2": 19
}
my_dict = {
"name": "王五",
"age": 21
}
my_list = [1, 2, 3, 4, 5, 6, 7]
list_index = 1
return render_template("index.html",
# 键值对
name1="张三", age1=18,
# 不定长参数
**data,
# 参数值除了可以是string和int类型外,还可以是dict,list等类型,只要这个类型能被 str() 方法转换为一个字符串就可以。
# dict类型
mydict=my_dict,
# list类型
mylist=my_list,
index=list_index
) if __name__ == '__main__':
app.run()
模板代码
模板放在templates目录下,名字为index.html。使用 {{ 参数名 }} 的方式进行插值操作。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- string 和 int取值方式 -->
<div>{{ name1 }}:{{ age1 }}</div>
<div>{{ name2 }}:{{ age2 }}</div> <!-- dict取值方式 -->
<div>{{ mydict }}</div>
<div>{{ mydict["name"]}}:{{ mydict.age }}</div> <!-- list取值方式 -->
<div>{{ mylist }}</div>
<div>{{ mylist[0] }}</div>
<div>{{ mylist[index] }}</div> </body>
</html>
渲染效果
二、过滤器
过滤器的本质就是函数。有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化、运算等等,而在模板中是不能直接调用 Python 中的某些方法,那么这就用到了过滤器。
过滤器的使用方式为:变量名 | 过滤器。 过滤器名写在变量名后面,中间用 | 分隔。如:{{variable | capitalize}},这个过滤器的作用:把变量variable的值的首字母转换为大写,其他字母转换为小写。
字符串过滤器
safe:禁用转义html标签
<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> reverse:字符串反转
<p>{{ 'olleh' | reverse }}</p> format:格式化输出
<p>{{ '%s is %d' | format('name',17) }}</p> striptags:渲染之前把值中所有的HTML标签都删掉
<p>{{ '<em>hello</em>' | striptags }}</p> truncate: 字符串截断
<p>{{ 'hello every one' | truncate(9)}}</p>
列表过滤器
irst:取第一个元素
<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>
语句块过滤(不常用)
{% filter upper %}
this is a Flask Jinja2 introduction
{% endfilter %}
自定义过滤器
过滤器的本质是函数。当模板内置的过滤器不能满足需求,可以自定义过滤器。
自定义过滤器有两种实现方式:
- 使用Flask应用对象的add_template_filter方法
- 使用Flask应用对象的template_filter装饰器
注意,自定义的过滤器名称如果和内置的过滤器重名,会覆盖内置的过滤器。
实现方式一:通过调用应用程序实例的add_template_filter方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称。
# 间隔截取列表
def filter_double_sort(ls):
return ls[::2] # 第一个参数为过滤器函数,
# 第二个参数为模板中使用的过滤器名字)
app.add_template_filter(filter_double_sort, 'double_2')
实现方式二:使用Flask应用对象的template_filter装饰器实现自定义过滤器。装饰器传入的参数是自定义的过滤器名称。
# 装饰器中的参数为模板中使用的过滤器名字
@app.template_filter('db2')
def filter_double_sort(ls):
return ls[::2]
使用自定义过滤器
<p>add_template_filter函数:{{ mylist | double_2 }}</p>
<p>template_filter装饰器:{{ mylist | db2}}</p>
三、Flask-WTF表单扩展
在介绍Flask-WTF表单扩展前,我们先不使用Flask-WTF简单实现一个注册表单:
模板文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- form标签不使用action属性时,发送的请求被当前视图函数接收 -->
<form method='post'>
<div><input type="text" name="username" placeholder='用户名'></div>
<div><input type="password" name="password" placeholder='密码'></div>
<div><input type="password" name="password2" placeholder='确认密码'></div>
<input type="submit">
</form>
</body>
</html>
视图函数如下:
from flask import Flask, render_template, request, session, redirect, url_for app = Flask(__name__)
app.config["SECRET_KEY"] = "asd" @app.route("/login/")
def login():
return F"{session.get('user')} 注册成功" @app.route("/register/", methods=["get", "post"])
def register():
if request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
password2 = request.form.get("password2")
session["user"] = username
return redirect(url_for("login"))
else:
return render_template("register.html") if __name__ == '__main__':
app.run()
测试效果:
上图中,即使密码与确认密码不一致,但仍能注册成功,这是因为我们没有在视图中验证表单的数据,为了保证参数的合规性,我们需要对每个使用if来进行校验,这是很繁琐的。
而Flask-WTF扩展不仅可以帮助我们在视图中验证表单的数据,还可以使用该扩展进行CSRF验证,帮助我们快速定义表单模板。
使用Flask-WTF表单扩展,需要自行安装,安装命令如下:
pip install flask-wtf
并且还需要配置参数SECRET_KEY,当CSRF(跨站请求伪造)保护激活的时候,CSRF_ENABLED设置会根据设置的密匙生成加密令牌。
接下来,我们使用Flask-WTF实现表单。
模板文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post">
<!-- 设置csrf_token -->
{{ form.csrf_token }} {{ form.user_name.label }}
<p>{{form.user_name}}</p>
{% for msg in form.user_name.errors %}
<p>{{msg}}</p>
{% endfor %} {{ form.password.label }}
<p>{{form.password}}</p>
{% for msg in form.password.errors %}
<p>{{msg}}</p>
{% endfor %} {{ form.password2.label }}
<p>{{form.password2}}</p>
{% for msg in form.password2.errors %}
<p>{{msg}}</p>
{% endfor %} {{form.submit}}
</form>
</body>
</html>
视图函数如下:
from flask import Flask,render_template, redirect,url_for,session,request,flash # 导入wtf扩展的表单类
from flask_wtf import FlaskForm
# 导入自定义表单需要的字段
from wtforms import SubmitField, StringField, PasswordField
# 导入wtf扩展提供的表单验证器的验证函数
from wtforms.validators import DataRequired, EqualTo app = Flask(__name__)
app.config['SECRET_KEY'] = 'asd' @app.route("/login/")
def login():
return F"{session.get('user')} 注册成功" # 定义注册表单类,该类继承FlaskForm
class RegisterForm(FlaskForm):
# 字段类型:
# StringField 对应 type="text"
# PasswordField 对应 type="password"
# SubmitField 对应 type="submit"
# 字段参数说明:
# label:字段的名称
# validators:验证器
# DataRequired(message):验证数据不能为空。message为校验不通过的提示
# EqualTo(fieldname, message):验证与指定字段fieldname的值相等,message为校验不通过的提示。
user_name = StringField(label=u"用户名", validators=[DataRequired(u"用户名不能为空")])
password = PasswordField(label=u"密码", validators=[DataRequired(u"密码不能为空")])
password2 = PasswordField(label=u"确认密码", validators=[DataRequired(u"确认密码不能为空"),
EqualTo("password", u"两次密码不一致")])
submit = SubmitField(label=u"提交") # 定义根路由视图函数,生成表单对象,获取表单数据,进行表单数据验证
@app.route('/register/', methods=['GET', 'POST'])
def register():
# 创建表单对象, 如果是post请求,前端发送了数据,flask会把数据在构造form对象的时候,存放到对象中
form = RegisterForm() # 判断form中的数据是否合理
# 如果form中的数据完全满足所有的验证器,则返回True,否则返回False
if form.validate_on_submit():
# 表示验证合格
# 提取数据
uname = form.user_name.data
pwd = form.password.data
pwd2 = form.password2.data
print(uname, pwd, pwd2)
session["user"] = uname
return redirect(url_for("login"))
# 如果校验为False,则返回模板数据
return render_template("register.html", form=form) if __name__ == '__main__':
app.run(debug=True)
测试效果:
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 | 验证输入值不在可选列表中 |
四、控制语句
模板中的if控制语句
@app.route('/user')
def user():
user = 'flsk'
return render_template('user.html',user=user)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if user %}
<h1> hello {{user}} </h1>
{% else %}
<h1> welcome to flask </h1>
{% endif %}
</body>
</html>
模板中的for循环语句
@app.route('/loop')
def loop():
fruit = ['apple','orange','pear','grape']
return render_template('loop.html',fruit=fruit)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% for index in fruit %}
<li>{{ index }}</li>
{% endfor %}
</ul>
</body>
</html>
五、宏、继承、包含
5.1 宏
类似于python中的函数,宏的作用就是在模板中重复利用代码,避免代码冗余。
Jinja2支持宏,还可以导入宏,需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免重复。
定义宏
{% macro input() %}
<input type="text"
name="username"
value=""
size="30"/>
{% endmacro %}
调用宏
{{ input() }}
定义带参数的宏
{% macro input(name,value='',type='text',size=20) %}
<input type="{{ type }}"
name="{{ name }}"
value="{{ value }}"
size="{{ size }}"/>
{% endmacro %}
调用宏,并传递参数
{{ input(value='name',type='password',size=40)}}
把宏单独抽取出来,封装成html文件,其它模板中导入使用
文件名可以自定义,比如:定义macro.html,文件内代码如下
{% macro function() %}
<input type="text" name="username" placeholde="Username">
<input type="password" name="password" placeholde="Password">
<input type="submit">
{% endmacro %}
在其它模板文件中先导入,再调用
{% import 'macro.html' as func %}
{% func.function() %}
5.2 继承
模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。
{% block top %}``{% endblock %}
标签定义的内容,相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。
子模板使用extends指令声明这个模板继承自哪?父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()。
父模板: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 %}
5.4 宏、继承、包含
- 宏(Macro)、继承(Block)、包含(include)均能实现代码的复用。
- 继承(Block)的本质是代码替换,一般用来实现多个页面中重复不变的区域。
- 宏(Macro)的功能类似函数,可以传入参数,需要定义、调用。
- 包含(include)是直接将目标模板文件整个渲染出来。
Flask_Jinja2模板(九)的更多相关文章
- Sharepoint2013搜索学习笔记之自定义结果显示模板(九)
搜索结果通过套用定义好的显示模板来展示结果,显示模板由js和html组成,我们可以通过修改显示模板,然后将修改好的显示模板跟搜索结果绑定起来,来修改搜索结果的显示效果,例子如下图: 修改前 修改后 第 ...
- PowerPoint基础
一.基础 默认后缀ppt,pptx office2003和以后的版本只支持ppt, 可以将pptx另存为ppt97-2003 二.修改PPT尺寸 三.新建幻灯片 四.字体与段落设置 五.主题与字体 六 ...
- [转载]SharePoint 2013搜索学习笔记之自定义结果源
搜索中心新建好之后在搜索结果页上会默认有所有内容,人员,对话,视频这四个结果分类,每个分类会返回指定范围的搜索结果,这里我再添加了部门日志结果分类,搜索这个分类只会返回部门日志内容类型的搜索结果,要实 ...
- zabbix自动发现监控mysql
一. 数据库给只读权限 1.1 grant usage on *.* to 'zabbix'@'127.0.0.1' identified by 'zabbix'; flush privileges; ...
- zabbix主机自动注册
一.主机自动注册的流程 zabbix agent指定server active主动自己的信息提供给zabbix_server,zabbix_server根据提供的信息自动添加主机,方便. 二. lin ...
- 学习Microsoft Visio(1)
基础篇 一.认识Visio 1.Visio是什么 Visio最初属于Visio公司,该公司成立于1990年9月.1992年,公司更名为Shapeware.同年11月,它发布了他们公司的第一个产品:Vi ...
- ApacheCN PythonWeb 译文集 20211110 更新
Django By Example 中文版 1 创建一个博客应用 2 为博客添加高级功能 3 扩展你的博客应用 4 创建一个社交网站 5 分享内容到你的网站 6 跟踪用户动作 7 构建在线商店 8 管 ...
- ApacheCN PythonWeb 译文集 20211028 更新
Django By Example 中文版 1 创建一个博客应用 2 为博客添加高级功能 3 扩展你的博客应用 4 创建一个社交网站 5 分享内容到你的网站 6 跟踪用户动作 7 构建在线商店 8 管 ...
- ThinkPHP框架视图详细介绍 View 视图--模板(九)
原文:ThinkPHP框架视图详细介绍 View 视图--模板(九) 视图也是ThinkPHP使用的核心部分: 一.模板的使用 a.规则 模板文件夹下[TPL]/[分组文件夹/][模板主题文件夹/]和 ...
随机推荐
- js 长按鼠标左键实现溢出内容左右滚动滚动
var nextPress, prevPress; // 鼠标按下执行定时器,每0.1秒向左移一个li内容的宽度 function nextDown() { nextPress = setInterv ...
- idea集成开发工具快捷键大全
1 执行(run) alt+r 2 提示补全 (Class Name Completion) ...
- 删除空行(嵌套)(Power Query 之 M 语言)
数据源: "姓名""基数""个人比例""个人缴纳""公司比例""公司缴纳"&qu ...
- CF158A Next Round 题解
Content 有 \(n\) 个人参加比赛,第 \(i\) 名在第一轮的分数是 \(a_i\)(保证 \(a_i\geqslant a_{i+1}\))已知下一轮预计能进 \(k\) 人,当然如果有 ...
- CF289B Polo the Penguin and Matrix 题解
Content 有一个 \(n\times m\) 的矩阵 \(A\),每次操作你可以将某一个元素增加或减少 \(d\),求是所有元素相等的最小操作次数,或者不存在这样的操作方案. 数据范围:\(1\ ...
- Docker容器自动更新
前言: Watchtower 是一个可以实现自动化更新 Docker 基础镜像与容器的实用工具.它监视正在运行的容器以及相关的镜像,当检测到registry中的镜像与本地的镜像有差异时,它会拉取最新 ...
- 选课系统V1.0
tree . . ├── bin │ ├── __init__.py │ └── start.py #启动文件 ├── conf │ ├── __init__.py │ └── set ...
- Linux(debian7)操作基础(四)之CPU频率调整 Linux系统CPU频率调整工具使用
在Linux中,内核的开发者定义了一套框架模型来完成CPU频率动态调整这一目的,它就是CPU Freq系统.如下为CPU的几种模式(governor参数): ondemand:系统默认的超频模式,按需 ...
- org.apache.jasper.runtime.ELContextImpl cannot be cast to org.apache.jasper.el.ELContextImpl
org.apache.jasper.runtime.ELContextImpl cannot be cast to org.apache.jasper.el.ELContextImpl错误怎么解决: ...
- ajax 有终止请求 abort 那 axios 有没有,怎么实现
见代码 class View extends Component { constructor(props){ super(props); this.state = { cancel:null, can ...