文章目录
- 一、模板与自定义过滤器
- 1 模板
- 2 过滤器
- 转义过滤器讲解
- 3自定义过滤器
- 二、表单
- 1表单
- 2表单扩展
- 三、创建表单模型类与模板使用
- 3.1 表单模型类
- 四 、使用表单接受并检验参数
- 五、模板宏的使用
- 六 、宏定义在外部的使用
- 七 :模板继承与包含
- 继承
- 包含include
- 八 、闪现
- 在模板中获取闪现信息
- 简单的在模板中实现获取闪现信息
一、模板与自定义过滤器
1 模板
使用模块可以在templates新建一个html文件,如index.html文件,如基础模板分别为:
程序基础模板:
index.html基础模板:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>name = {{name}}</p>
</body>
</html>
后端代码
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
data = {
"name": "余登武",
}
return render_template("index.html", **data)
if __name__ == '__main__':
app.run(debug=True, port=8000)
html文件中{{name}}
表示的是一个变量,告诉模板引擎渲染时添加变量的值。 **data
两个 **
表示字符串变量。
若传递的参数有列表或字典,仍可按照正常的取值方式,例如:字典.get
,["关键词"]
;列表[索引]
。
程序:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
data = {
"name": "python",
"my_dict": {"itchat": "Beijing"},
"my_list": [1, 2, 3, 4, 5],
"my_int": 0
}
return render_template("index.html", **data)
if __name__ == '__main__':
app.run(debug=True, port=8000)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>name = {{name}}</p>
<p>my_dict:itchat = {{my_dict["itchat"]}}</p>
<p>my_dict:itchat = {{my_dict.get("itchat")}}</p>
<p>my_list:my_int = {{my_list[my_int]}}</p>
<p>my_list:my_int = {{my_list[1]}}</p>
</body>
</html>
2 过滤器
flask有着自己的过滤器模块,如下展示:(即在变量后面加|过滤器名
,连续过滤则|过滤器名|过滤器名
)
格式{{变量|过滤器名}}
字符串过滤器`
- safe:禁止转义
- capitalize:把变量值的首字母转成大写,其余字母转小写
- lower:把值转成小写
- upper:把值转成大写
- title:把值中的每个单词的首字母都转成大写
- trim:把值的首尾空格去掉
- reverse:字符串反转
- format:格式化输出
- striptags:渲染之前把值中所有的HTML标签都删掉
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>name = {{name}}</p>
<p>my_dict:itchat = {{my_dict["itchat"]}}</p>
<p>my_dict:itchat = {{my_dict.get("itchat")}}</p>
<p>my_list:my_int = {{my_list[my_int]}}</p>
<p>name = {{name | upper}}</p>
<p>my_dict:itchat = {{my_dict.get("itchat") | lower | reverse}}</p>
</body>
</html>
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
data = {
"name": "python",
"my_dict": {"itchat": "Beijing"},
"my_list": [1, 2, 3, 4, 5],
"my_int": 0
}
return render_template("index.html", **data)
if __name__ == '__main__':
app.run(debug=True, port=8000)
转义过滤器讲解
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post">
<textarea name="text"></textarea>
<input type="submit" value="提交">
</form>
输入文本是:{{text}}
</body>
</html>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Author: yudengwu(余登武)
# @Date : 2023/4/9
#@email:1344732766@qq.com
from flask import Flask, render_template,request
app = Flask(__name__)
@app.route("/",methods=["GET","POST"])
def index():
text=""
if request.method =="POST":
text=request.form.get("text")
return render_template("index.html", text=text)
if __name__ == '__main__':
app.run(debug=True)
查看源代码,发现已被转义(带html格式文本中代码被转义为纯文本)
如果加入禁止转义
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post">
<textarea name="text"></textarea>
<input type="submit" value="提交">
</form>
输入文本是:{{text|safe}}
</body>
</html>
结果为 <font color="red>123</font>
代码被很好执行啦
3自定义过滤器
若flask自带的过滤器模块满足不了要求,可以使用自定义的过滤器方法
- 第一种方法,编写相关函数添加进过滤器模块中:
- 第二种方法,使用装饰器的方法给过滤器模块添加新功能:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
data = {
"name": "python",
"my_dict": {"itchat": "Beijing"},
"my_list": [1, 2, 3, 4, 5],
"my_int": 0
}
return render_template("index.html", **data)
# 自定义过滤器
"""第一种方法"""
# 1.编写过滤器函数
def list_step_2(li):
return li[::2]
# 注册过滤器
"""app.template_filter第一个参数为过滤器函数,过滤器函数名字"""
app.add_template_filter(list_step_2, "li2")
"""第二种方法 装饰器"""
@app.template_filter("li3")
def list_step_3(li):
return li[::3]
if __name__ == '__main__':
app.run(debug=True, port=8000)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>name = {{name}}</p>
<p>my_dict:itchat = {{my_dict["itchat"]}}</p>
<p>my_dict:itchat = {{my_dict.get("itchat")}}</p>
<p>my_list:my_int = {{my_list[my_int]}}</p>
<p>name = {{name | upper}}</p>
<p>my_dict:itchat = {{my_dict.get("itchat") | lower | reverse}}</p>
<p>guolvqi = {{my_list | li2}}</p>
<P>guolvqi2 = {{my_list | li3}}</P>
</body>
</html>
二、表单
1表单
Web 表单是 Web 应用程序的基本功能,它是 HTML 页面中负责数据采集的部件。通常情况下,表单有三个 部分组成,分别是表单标签、表单域、表单按钮。表单允许用户输入数据,负责HTML 页面数据采集,通过表单 将用户输入的数据提交给服务器。 实际上,要让模板动态呈现表单数据,可使用 request.form 属性传递数据过来即可。
py文件
# 引入Flask
from flask import Flask, render_template, request
# 创建Flask实例
app = Flask(__name__)
app.debug = True
@app.route('/logintest/')
def login_test():
return render_template("login_test.html")
@app.route('/handle/', methods=['POST'])
def handle():
# form 表单信息
form_results = request.form
# 显示
name = form_results.get('name')
age = form_results.get('age')
pro = form_results.get('pro')
description = form_results.get('description')
# 数据
results = {
'姓名': name,
'年龄': age,
'程序成绩': pro,
'自我评价': description,
}
return render_template("results.html", results=results)
if __name__ == '__main__':
# 在测试环境下开启服务
app.run()
在 templates目录下创建对应的两个html文件:
login_test.html
results.html
login_test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录测试</title>
</head>
<body>
<h1>表单显示</h1>
{# action="handle" 意思是从路由访问这个函数 #}
{# 注意这个 /handle/ 要跟路由保持一致,斜杠也要对应上否则会出错 #}
<form method="post" action="/handle/">
<p>姓名:<input type="text" name="name" ></p>
<p>年龄:<input type="number" name="age"></p>
<p>编程成绩:<input type="number" name="pro"></p>
<p>自我评价:<textarea name="description" ></textarea> </p>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
results.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>显示结果</title>
</head>
<body>
<h1>结果</h1>
<table border="1">
{% for key,value in results.items() %}
<tr>
<td>{{ key }}</td>
<td>{{ value }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
上述代码无法进行表单验证(即验证有没有填写,填写是否正确等,如果需要验证,则需要在py文件里加入多个if语句(如判断是不是文字,是否填写))
2表单扩展
下面通过 Flask-WTF
扩展库、使用 Flask-WTF 实现表单
在 Flask 框架中,为了快速处理 Web 表单,我们一般使用 Flask-WTF 库,它封装了 WTForms ,并且它有验证表单数据的功能。若要使用该库,则需要先进行安装 pip install Flask-WTF
还需特别注意的是,使用 Flask-WTF 库时,必须要配置参数 SECRET_KEY 属性。当设定好了 SECRET_KEY 后,可用来生成加密令牌,当 CSRF 激活时,该设置会根据设置的密匙生成加密令牌。
*使用 Flask-WTF 实现表单
对于 Flask-WTF 扩展库,可以定义模型,此时需要继承自 Form 基类,需从 flask.ext.wtf 中导入,且字段和验 证函数可以直接从 WTForms 包中导入。此处,因 Flask 与 werkzeug 的兼容性问题,因此要安装 werkzeug pip install werkzeug
py文件
# 引入Flask
from flask import Flask, render_template, request, flash
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import DataRequired, EqualTo
# 创建Flask实例
app = Flask(__name__)
app.debug = True
# 密钥
app.config["SECRET_KEY"] = "123abchello"
#自定义表单类、文本字段、密码字段、提交按钮
class LoginForm(FlaskForm):
# 用户名
username = StringField(label=u'用户名', validators=[DataRequired()])
# 密码
password = PasswordField(label=u"密码", validators=[DataRequired()])
# 确认密码
repwd = PasswordField(label=u'确认密码', validators=[DataRequired(), EqualTo("password", "两次密码输入不一致")])
# 提交
submit = SubmitField(label=u"提交")
@app.route("/index/")
def index():
post_form = LoginForm() #类对象
return render_template("index.html", form=post_form) #form=post_form 把模板变量传过去
@app.route("/login/", methods=["GET", "POST"])
def login():
if request.method == "POST":
post_form = LoginForm()#类对象 把用户填写的数据赛到这个对象来的
if post_form.validate_on_submit(): #validate检验
name = post_form.username.data
password = post_form.password.data
repwd = post_form.repwd.data
print("姓名:", name)
print("密码:", password)
print("确认密码:", repwd)
flash(f"欢迎{name}")
else:
flash("信息有误,请重新输入!")
return render_template("results.html", form=post_form)
if __name__ == '__main__':
# 在测试环境下开启服务
app.run()
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>wtf_login</title>
</head>
<body>
<h1>登录表单</h1>
<form method="post" action="/login/">
{{ form.csrf_token() }}
<p><b>{{ form.username.label }}</b>{{ form.username }}</p>
<p><b>{{ form.password.label }}</b>{{ form.password }}</p>
<p><b>{{ form.repwd.label }}</b>{{ form.repwd }}</p>
<p><b>{{ form.submit() }}</b></p>
{% for msg in get_flashed_messages() %}
{{ msg }}
{% endfor %}
</form>
</body>
</html>
results.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>你好\(@^0^@)/你好</title>
</head>
<body>
{% for msg in get_flashed_messages() %}
<h1>{{ msg }}</h1>
{% endfor %}
</body>
</html>
如果前后密码不一致
讲解
闪现 :请求完成后给用户的提醒消息,flask的核心特性, flash函数实现效果
- 视图函数中调用flash()方法
- html中要使用get_flashed_messages()
flask-wtf的表单验证方法form.validate_on_submit()
会对进行CSRF验证,如果form.csrf_token()
代码写错相当于没有form.csrf_token()
进行渲染该字段,那么flask-wtf在验证表单签名时就会一直不通过。
CSRF攻击的大致方式如下:某用户登录了A网站,认证信息保存在cookie中。当用户访问攻击者创建的B网站时,攻击者通过在B网站发送一个伪造的请求提交到A网站服务器上,让A网站服务器误以为请求来自于自己的网站,于是执行相应的操作,该用户的信息便遭到了篡改。总结起来就是,攻击者利用用户在浏览器中保存的认证信息,向对应的站点发送伪造请求。在前面学习cookie时,我们介绍过用户认证通过保存在cookie中的数据实现。在发送请求时,只要浏览器中保存了对应的cookie,服务器端就会认为用户已经处于登录状态,而攻击者正是利用了这一机制。
三、创建表单模型类与模板使用
if 语句
{%if%} {%endif%}
for语句
{%for item in samples %} {% endfor %}
3.1 表单模型类
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>wtf_login</title>
</head>
<body>
<h1>登录表单</h1>
<form method="post" >
{{ form.csrf_token() }}
<p><b>{{ form.username.label }}</b>{{ form.username }}
{% for msg in form.username.errors%}
{{msg}}}
{% endfor%}
</p>
<p><b>{{ form.password.label }}</b>{{ form.password }}
{% for msg in form.password.errors%}
{{msg}}}
{% endfor%}
</p>
<p><b>{{ form.repwd.label }}</b>{{ form.repwd }}
{% for msg in form.repwd.errors%}
{{msg}}}
{% endfor%}
</p>
<p><b>{{ form.submit() }}</b></p>
{% for msg in get_flashed_messages() %}
{{ msg }}
{% endfor %}
</form>
</body>
</html>
py文件
# 引入Flask
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import DataRequired, EqualTo
# 创建Flask实例
app = Flask(__name__)
app.debug = True
# 密钥
app.config["SECRET_KEY"] = "123abchello"
#自定义表单类、文本字段、密码字段、提交按钮
class LoginForm(FlaskForm):
"""
自定义表单类
label=u'标签', validators=验证器(列表格式)
"""
# 用户名
username = StringField(label=u'用户名', validators=[DataRequired()])
# 密码
password = PasswordField(label=u"密码", validators=[DataRequired()])
# 确认密码
repwd = PasswordField(label=u'确认密码', validators=[DataRequired(), EqualTo("password", "两次密码输入不一致")])
# 提交
submit = SubmitField(label=u"提交")
@app.route("/index/")
def index():
post_form = LoginForm() #类对象
return render_template("index.html", form=post_form) #form=post_form 把模板变量传过去
if __name__ == '__main__':
# 在测试环境下开启服务
app.run()
结果
查看网页源代码
flask_wtf 已自动将表单类被渲染成html代码(前端语言)
四 、使用表单接受并检验参数
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>wtf_login</title>
</head>
<body>
<h1>登录表单</h1>
<form method="post">
{{ form.csrf_token() }}
<p><b>{{ form.username.label }}</b>{{ form.username }}
{% for msg in form.username.errors%}
{{msg}}}
{% endfor%}
</p>
<p><b>{{ form.password.label }}</b>{{ form.password }}
{% for msg in form.password.errors%}
{{msg}}}
{% endfor%}
</p>
<p><b>{{ form.repwd.label }}</b>{{ form.repwd }}
{% for msg in form.repwd.errors%}
{{msg}}}
{% endfor%}
</p>
<p><b>{{ form.submit() }}</b></p>
{% for msg in get_flashed_messages() %}
{{ msg }}
{% endfor %}
</form>
</body>
</html>
py文件
# 引入Flask
from flask import Flask, session,render_template,redirect,url_for
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import DataRequired, EqualTo
# 创建Flask实例
app = Flask(__name__)
app.debug = True
# 密钥
app.config["SECRET_KEY"] = "123abchello"
#自定义表单类、文本字段、密码字段、提交按钮
class LoginForm(FlaskForm):
"""
自定义表单类
label=u'标签', validators=验证器(列表格式)
"""
# 用户名
username = StringField(label=u'用户名', validators=[DataRequired()])
# 密码
password = PasswordField(label=u"密码", validators=[DataRequired()])
# 确认密码
repwd = PasswordField(label=u'确认密码', validators=[DataRequired(), EqualTo("password", "两次密码输入不一致")])
# 提交
submit = SubmitField(label=u"提交")
@app.route("/register/",methods=["GET","POST"])
def register():
post_form = LoginForm() #创建表单对象。如果是POST请求,前端发送了数据,flask会把数据在构造form对象时存放到对象中
#post_form.validate_on_submit判断form中的数据是否合理以及csrf验证
#如果验证成功,返回真,否则flase(验证失败和请求方式不是get)
if post_form.validate_on_submit():
#表示验证合格
# 提取数据
uname=post_form.username.data
pwd=post_form.password.data
pwd2=post_form.repwd.data
print(uname,pwd,pwd2)
#模拟登录
session["username"]=uname
return redirect(url_for("index")) #定向到登录成功界面
#如果失败
return render_template("register.html", form=post_form) #form=post_form 把模板变量传过去
@app.route("/index/")
def index():
user_name=session.get("urername","") #提取参数。默认空
return "你好,主人"
if __name__ == '__main__':
# 在测试环境下开启服务
app.run()
结果
登录界面
输入信息有错时
输入正确和cstf无误时
五、模板宏的使用
类似于python中的函数,宏的作用就是在模板中重复利用代码,避免代码冗余。
不带参数的宏
macro类似def
定义
{% macro input()%}
<input type="text" name="name" value="" size="30">
{% endmacro%}
使用
{{input()}}
带参数宏
定义 带参数
{% macro input1(type,value,size)%}
<input type="{{type}}" value="{{value}}" size="{{size}}">
{% endmacro%}
使用
{{input1(type="password",value="",size=50)}}
算例
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>定义不带参数宏</h1>
{% macro input()%}
<input type="text" value="" size="30">
{% endmacro%}
{{input()}}
<hr/>
<h1>定义 带参数</h1>
{% macro input1(type,value,size)%}
<input type="{{type}}" value="{{value}}" size="{{size}}">
{% endmacro%}
{{input1(type="password",value="",size=50)}}
<hr/>
<h1>定义 带参数(默认值)</h1>
{% macro input2(type="text",value="",size=50)%}
<input type="{{type}}" value="{{value}}" size="{{size}}">
{% endmacro%}
{{input2(type="password",value="123")}}
</body>
</html>
py文件
# 引入Flask
from flask import Flask, render_template
# 创建Flask实例
app = Flask(__name__)
app.debug = True
@app.route("/index/")
def index():
return render_template("index.html")
if __name__ == '__main__':
# 在测试环境下开启服务
app.run()
网页源代码
六 、宏定义在外部的使用
在其它模板文件中先导入,再调用
示例
macro1.html
该html只要宏部分
{% macro input4(type="text",value="",size=50)%}
<input type="{{type}}" value="{{value}}" size="{{size}}">
{% endmacro%}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>定义不带参数宏</h1>
{% macro input()%}
<input type="text" value="" size="30">
{% endmacro%}
{{input()}}
<hr/>
<h1>定义 带参数</h1>
{% macro input1(type,value,size)%}
<input type="{{type}}" value="{{value}}" size="{{size}}">
{% endmacro%}
{{input1(type="password",value="",size=50)}}
<hr/>
<h1>定义 带参数(默认值)</h1>
{% macro input2(type="text",value="",size=50)%}
<input type="{{type}}" value="{{value}}" size="{{size}}">
{% endmacro%}
{{input2(type="password",value="123")}}
<hr/>
<h1>外部宏</h1>
{% import "macro1.html" as m_input %}
{{m_input.input4()}}
</body>
</html>
核心代码
{% import "macro1.html" as m_input %}
{{m_input.input4()}}
py文件
# 引入Flask
from flask import Flask, render_template
# 创建Flask实例
app = Flask(__name__)
app.debug = True
@app.route("/index/")
def index():
return render_template("index.html")
if __name__ == '__main__':
# 在测试环境下开启服务
app.run()
七 :模板继承与包含
继承
先说模板的继承,定义一个父模板(命名为“father.html”),其格式为:
{% block 自定义名称 %}
{% endblock 自定义名称 %}
定义一个子模板(命名为“son.html”),其格式为:
{% extends "father.html" %}
{% block 自定义名称 %}
插入内容
{% endblock 自定义名称 %}
下面举一个例子进行说明:
定义文件father.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>——————第一个按钮——————{% block top %}{% endblock top %}</h1>
<h1>——————第二个按钮——————{% block hello %}{% endblock hello %}</h1>
</body>
</html>
定义文件son.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% extends "father.html" %}
{% block top %}
<br>
<input name="第一" type="text" value=" " size="50">
{% endblock top %}
{% block hello %}
<br>
<input name="第二" type="password" value="" size="50">
{% endblock hello %}
</body>
</html>
定义渲染的函数:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def top():
return "这是主页"
@app.route("/hello")
def index():
return render_template("son.html")
if __name__ == '__main__':
app.run(debug = True, port = 8000)
包含include
include语句:可以把一个模板引入到另外一个模板中
把一个模板引入到另外一个模板中,类似于把一个模板的代码copy到另外一个模板的指定位置
当定义了两个html文件时,通过include语句可以将这两个html内容加载到第三个html文件中。
{% include 'header.html' %}
主体内容
{% include 'footer.html' %}
## 输出的结果按照指定的位置顺序依次在网页模板上显示。
八 、闪现
在模板中获取闪现信息
Flask 提供了一个非常简单的方法来使用闪现系统向用户反馈信息。闪现系统使得在一个请求结束的时候记录一个信息,然后在且仅仅在下一个请求中访问这个数据,强调flask闪现是基于flask内置的session的,利用浏览器的session
缓存闪现信息。所以必须设置secret_key
。
简单的在模板中实现获取闪现信息
实例:
server.py
from flask import Flask, flash, redirect, render_template, \
request, url_for
app = Flask(__name__)
app.secret_key = 'some_secret'
@app.route('/')
def index():
return render_template('index.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != 'admin' or \
request.form['password'] != '123':
error = '登录失败'
else:
flash('恭喜您登录成功')
return redirect(url_for('index'))
return render_template('login.html', error=error)
if __name__ == "__main__":
app.run()
注意:这个 flash() 就可以实现在下一次请求时候,将括号内的信息做一个缓存。不要忘记设置secret_key
index.html 模板:
{% with messages = get_flashed_messages() %} # 获取所有的闪现信息返回一个列表
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<h1>主页</h1>
<p>跳转到登录页面<a href="{{ url_for('login') }}">登录?</a>
注意:{% with messages = get_flashed_messages() %} # 获取所有的闪现信息返回一个列表
这里是login.html 模板
<h1>登录页面</h1>
{% if error %}
<p class=error><strong>Error:</strong> {{ error }}
{% endif %}
<form action="" method=post>
用户名:
<input type=text name=username>
密码:
<input type=password name=password>
<p><input type=submit value=Login></p>
</form>