(上接 《Flask 框架学习(一)》)
3、表单
3.1 简单表单(不接收数据)
学习的时候我还是摒弃了工程格式,还是用最简单的结构比较清晰,比较好改动
xxx
|-templates文件夹
| |-(模板文件)
|-.py文件
首先我们需要在下载一个flask-wtf,安装之后我们才能开始表单的学习。
默认情况下,Flask-WTF保护各种形式的CSRF攻击。而为了实现CSRF保护,Flask-WTF需要应用程序去配置一个加密密钥。简单地说就是加上一句话
app.config['SECRET_KEY'] = 'bendawang'#这个值一定不能被轻易猜出
后面会说明具体加在什么地方以及具体的用途。
我先列出源码:
我们的结构如下:
xxx
|-templates文件夹
| |-base.html
| |-login.html
|-forms.py文件
|-views.py
模板文件分别如下:
base.html如下:
<html>
<head>
{%if title!=Null%}
<title>{{title}} is in</title>
{%else%}
<title>no one in</title>
{% endif %}
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
login.html如下:
{% extends "base.html"%}
{% block content %}
<form method="POST" action="/login">
{{form.hidden_tag()}}
<h2>输入用户名</h2>{{ form.name.label }} {{ form.name(size=20) }}
<input type="submit" value="Go">
</form>
{% endblock %}
forms.py
from flask_wtf import Form
from wtforms import TextField
from wtforms.validators import DataRequired
class MyForm(Form):
name = TextField('name', validators=[DataRequired()])
然后views.py如下:
from flask import Flask
from flask import render_template, flash, redirect,session
from forms import MyForm
app = Flask(__name__)
app.config['SECRET_KEY']='bendawang'
@app.route('/login', methods = ['GET', 'POST'])
def login():
form = MyForm()
return render_template('login.html',title = 'Bendawang',form=form)
app.run(debug=True)
然后我们先访问试试再来开始介绍,访问http://127.0.0.1:5000/login,然后得到如下:
说明我们的表单成功建好了
接下来我们来分析下源码
首先说forms.py,每个web表单是由继承自Form类的子类来展现的。该类在表单中定义了一组表单域,每个都表示为一个对象。每个表单域都可以连接到一个或多个validators;validators是一个用于检查用户提交的输入是否合法的函数。例如源码中的
validators=[DataRequired()]
DataRequired()验证确保提交的表单域不为空。
然后我们这个表单类就是新建了一个文本输入框,然后要求输入非空。至于我们的view.py除了加上了防护CRSF的密钥之外,就是实例化了一个form object,然后login.html文件就直接把实例化的form渲染出来了。
简单的说,只需要在模板合适的位置添加类似于 {{ form.field_name }} 这样的模板参数,相关字段就会在被定义的位置出现。另外还有一些字段是可以传参数,比如这个 name 字段,我们就添加了一个参数让它显示的宽度增加到20个字符。
由于我们没有在表单中定义一个提交功能的按钮,所以在这里只能以普通表单字段的方式来做了。不过一个按钮,在表单中跟任何数据都没有关系,的确也没有在表单类中定义的必要。
另外,还有
{{form.hidden_tag()}}
我们在配置中开启了CSRF(跨站伪造请求)功能,模板参数 {{ form.hidden_tag() }} 会被替换成一个具有防止CSRF功能的隐藏表单字段。在开启了CSRF功能后,所有模板的表单中都需要添加这个模板参数。 这也是很多错误的来源。
而view.py做的工作就是单纯的把表单的对象传给模板。另外还做了一件事
@app.route('/login', methods = ['GET', 'POST'])
就是让 Flask 明白需要支持 GET 和 POST 请求。methods
参数被添加到app.route
装饰器中,目的是让Flask注册视图函数为GET和POST请求处理程序到URL映射中。若methods
参数未给出,视图函数将只注册为GET请求。
3.2简单表单(接收数据)
在3.1的基础上,我们不做过多修改,只需要修改views.py,还是老规矩先贴源码好了
from flask import Flask
from flask import render_template, flash, redirect,session
from forms import MyForm
app = Flask(__name__)
app.config['SECRET_KEY']='bendawang'
@app.route('/login', methods = ['GET', 'POST'])
def login():
form = MyForm()
if form.validate_on_submit():
name = form.name.data
return render_template('login.html',title = 'Bendawang',form=form)
app.run(debug=True)
观察代码,表单的validate_on_submit()方法会在表单被提交且数据通过了所有验证的时候返回True。其他情况下validate_on_submit()返回False。而之前在form.py中定义Myform类的时候我们加上了一个非空的限制,所以这里的判断就只是一个非空的判断,然后通过form.name的data属性读取出输入框的值,当然我们获取之后并未进行操作,操作的话视情况而定这里不做赘述。
3.3简单表单(重定向)
我们要实现一个比如我们的之前输入界面输入一个值以后,然后验证,最后重定向到另一个成功的页面。这也是一般登录的基础。也是表单最常用的用途。当然也可以修改form标签的action属性,直接递交给另一个页面,这个和3.2类似,就不多说了。
这个方法还可以使用redirect响应POST请求来代替常规的响应来实现。
重定向是一个特殊类型的响应,使用URL来代替HTML代码字符串。当浏览器收到这个响应,它就会给重定向URL发出一个GET请求,然后显示页面。页面也许需要几毫秒的时间来加载,因为需要发送第二个请求给服务器,除此之外用户不会看到任何不同。现在最后一次请求为GET,所以刷新会像预期的那样。这个方法被称为Post/Redirect/Get模式。
但是这个方法带来了第二个问题。当应用程序处理POST请求,需要访问用户输入并保存在form.name.data中的名字,但是一旦该请求结束表单数据就会丢失。因为POST请求是通过重定向来处理,应用程序需要存储名字,以便重定向后的请求可以得到它并使用它来创建真实的响应。
应用程序可以“记住”一些变量从一个请求到另一个请求通过将变量保存到用户会话中,对于每一个连接过来的客户端它都是一个私有存储区域。作为一个与请求上下文关联的变量之一,用户会话已经在第二章中介绍过了。它被称为会话并可以像Python标准字典那样访问。
注:默认情况下,用户会话被存放于客户端的cookies,使用配置的SECRET_KEY来加密签名。任何篡改cookie内容将会使签名无效,从而使会话失效。
即比如我们再/login
页面输入之后要跳转到另一个/success
的页面,怎样同时能够将我们输入的值传递过去。
首先我们要是表格具有跳转,那么login.html要简单的修改,把form标签的action属性改成如下:
<form method="POST" action="/success">
这样在我们点击按钮之后,就会从127.0.0.1/login
跳转至127.0.0.1/success
,
然后就是我们的view.py,改动之后如下:
from flask import Flask
from flask import render_template, flash, redirect,session
from forms import MyForm
app = Flask(__name__)
app.config['SECRET_KEY']='bendawang'
@app.route('/login', methods = ['GET', 'POST'])
def login():
form = MyForm()
if form.validate_on_submit():
session['name']=form.name.data
return redirect('/success')
return render_template('login.html',title = 'Sign In',form=form)
@app.route('/success',methods = ['GET', 'POST'])
def success():
name=session.get('name')
return render_template('success.html',title = name)
app.run(debug=True)
因为使用了redirect,所以我们还需要写一个success.html模板页面,如下:
<html>
<head><title>success!</title><head>
<body>
{% if title %}
<h1>welcome {{title}}</h1>
{% else %}
<h1>welcome guest</h1>
{% endif %}
</body>
</html>
success.html模板没什么可说的,而我们来分析一下上面的views.py,我们在递交请求的时候,由于表单的action还是设置的是/login,所以提交还是会交给/login页面,然后我们的validate_on_submit()此时会返回TRUE,进入if语句,在其中,我们用了session['name']
来保存住name的值,然后重定向给了/success
页面,然后再用session.get(‘name’)读取出我们之前输入的值,从而完成参数的传递。
3.4简单表单(消息提示)
有时候在请求完成后给用户一个提示消息是非常有用的。例如当你在网站提交登录表单出现错误的时候服务器响应渲染登录表单并伴随一条消息,告知你的用户名或密码无效。
此处我们以验证两次输入是否一样为例,如果不一样就在原页面打印wrong!
如果一样就跳转到/success
页面。
首先我们依旧使用这个文件结构
xxx
|-templates文件夹
| |-success.html
| |-base.html
| |-login.html
|-forms.py文件
|-views.py
base.html
以及success.html
和3.1
贴出的源码一样。
login.html如下
{% extends "base.html"%}
{% block content %}
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
{{ message }}
</div>
{% endfor %}
<form method="POST" action="/login">
{{ form.hidden_tag() }}
<h2>密码:</h2>{{ form.oldname.label }} {{ form.oldname(size=20) }}
<h2>重复密码:</h2>{{ form.name.label }} {{ form.name(size=20) }}
<input type="submit" value="Go">
</form>
{% endblock %}
然后form.py如下:
from flask_wtf import Form
from wtforms import TextField
from wtforms.validators import DataRequired
class MyForm(Form):
name = TextField('name', validators=[DataRequired()])
oldname = TextField('oldname', validators=[DataRequired()])
然后views.py如下:
from flask import Flask
from flask import render_template, flash, redirect,session
from forms import MyForm
app = Flask(__name__)
app.config['SECRET_KEY']='bendawang'
@app.route('/login', methods = ['GET', 'POST'])
def login():
form = MyForm()
if form.validate_on_submit():
if(form.name.data==form.oldname.data):
session['oldname']=form.oldname.data
session['name']=form.name.data
return redirect('/success')
else:
flash('Wrong!')
return render_template('login.html',title = 'Sign In',form=form)
@app.route('/success',methods = ['GET', 'POST'])
def success():
name=session.get('name')
return render_template('success.html',title = name)
app.run(debug=True)
接下来可以先运行测试一下
然后我们上下均输入123,得到
如果上下不一样的话,得到
提示错误。
说明任务达到,接下来我们来分析一下代码,form.py没什么分析的。
直接看views.py,我们加了一个判断,如果两个框输入的值相等,那么就重定向,否则在原页面flash出一个wrong出来。这样还不够,调用flash()还不能获取并显示消息,渲染消息最好的地方是在基础模板中,因为这可以使得所有页面都可以使用这些消息。Flask提供get_flashed_messages()
函数给模板去接收消息并渲染它们,就像login.html里展示的那样。
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
{{ message }}
</div>
{% endfor %}