您的位置:首页 > 其它

Flask之Web表单使用

2017-04-29 22:40 309 查看

Web表单使用

@(Flask)

request对象包含客户端发出的所有请求信息。

request.form能获取POST请求中提交的表单数据。

使用的包

Flask-WTF可以把处理Web表单的过程变成愉快的体验。

pip install flask-wtf


CSRF:跨站请求伪造保护

实现CSRF保护的方法:设置秘钥。

简单的做法是写到代码中,但是这种做法不够安全最好是保存在环境变量中,这类技术后序会继续再谈。

#hello.py
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'


注意到,这里用到了
app.config
字典,Python中,几乎可以见名之意,这里也不例外。

app.config的作用:存储框架、扩展、程序本身的配置变量。

写入配置值的方法:

标准字典句法

从文件或环境导入配置值

值得注意的是,
SECRET_KEY
配置变量是通用秘钥

这个通用,指的是可在Flask和多个第三方扩展中使用。

表单类

每个Web表单都由一个继承自Form的类表示。

这个类定义了表单中的一组字段,每个字段都用对象表示。

字段对象可以附属一个或多个验证函数

验证函数的作用:验证用户提交的输入值是否符合要求。

#简单的Web表单,包含一个文本字段和一个提交按钮
from flask_wtf import Form
from wtforms import StringField,SubmitField
from wtforms.validators import Required
class NameForm(Form):
name = StringField('What is your name?', validators=[Required()])
submit = SubmitField('Submit')


理解:表单中的字段都定义为类变量

其中类变量的值是相应字段类型的对象。

这里的name字段,对应的是StringField的对象;

submit按钮是SubmitField的对象。

StringField类表示的是属性为
type="text"
<input>
元素。

SubmitField类表示的是是属性为
type="submit"
<input>
元素。

同时值得关注的是在
StringField
构造函数中有一个可选参数:
validators
,而且是以数组形式,正对应了前面说的一个字段可以有一个或者多个验证函数。

WTForms支持的HTML标准字段

StringField 文本字段

TextAreaField 多行文本字段

PasswordField 密码文本字段

HiddenField 隐藏文本字段

DateField 值为datetime.date格式的文本字段

DateTimeField 值为datetime.datetime格式的文本字段

IntegerField 值为整数的文本字段

DecimalField 值为decimal.Decimal的文本字段

FloatField 值为浮点数的文本字段

BooleanField 复选框,值为True

RadioField 一组单选框

SelectField 下拉列表

SelectMultipleField 下拉列表,可选择多个值

FileField 文件上传字段

SubmitField 表单提交按钮

FormField 将表单作为字段嵌入另一个表单

FieldList 一组指定类型的字段(不是很懂这个)

WTForms内建的验证函数

Email 验证电子邮件地址

EqualTo 比较两个字段的值,常用于两次密码进行确认

IPAddress 验证IPv4网络地址

Length 验证输入字符串的长度

NumberRange 验证输入的值在数字范围内

Optional 无输入值时跳过其他验证函数

Required 确保字段有数据

Regexp 使用正则表达式验证输入值

URL 验证URL

AnyOf 确保输入值在可选值列表内

NoneOf 确保输入值不再可选值列表中

表单渲染为HTML

表单字段是可以调用的。我们前面知道,视图函数通过路由中的动态参数可以与代码中的参数进行传递:

@app.route('/user/<name>')
def user(name):
return render_template('user.html',name=name)


左边的name是模板中的变量,右边的是视图函数传递的。

表单类写在xx.py中了,所以这个也是可以通过相似的方法传递的。

模板总共调用form实例后,即可以渲染为HTML用于客户端显示。

先不看传递的实现细节,而是假设视图函数将NameForm(这是我们前面写的表单类)实例通过参数form**传入了模板**,在模板中就可以生成表单。

<form method="POST">
{{ form.hidden_tag() }}
{{ form.name.label  }} {{ form.name() }}
{{ form.submit() }}
</form>


这种是极其简陋的表单,那么样式的控制自然需要交给CSS来做,做法是:将参数传入渲染字段的函数,传进来的参数会被转换成子弹的HTML属性

e.g :

<form method="POST">
{{ form.hidden_tag() }}
{{ form.name.label  }} {{ form.name(id='my-text-field') }}
{{ form.submit() }}
</form>


这样,name字段就会被渲染成含有一个id属性的元素。CSS样式表根据这个id就可以做进一步的渲染了。

这是比较粗糙而原始的做法,既然借助了Bootstrp,自然表单修饰也可以选择它来做。

Bootstrap渲染表单

Flask-Bootstrap提供了一个高顿的辅助函数。

{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form) }}


这里import指令导入的是模板文件,这个模板文件中定义了使用Bootstrap渲染WTF表单的辅助函数:
wtf.quick_form(form)
将会用默认的样式渲染传入的表单。

模板写法案例:

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{% block title %}Flasky {% endblock %}

{% block page_content %}
<div class="page-header">
<h1>
Hello, {% if name %} {{ name }} {% else %} Stranger {% endif %}!
</h1>
</div>
{{ wtf.quick_form(form) }}
{% endblock %}


注意这里模板条件语句的写法。

格式是:
{% if condition %}...{% else %}...{% endif %}


条件的结果是
True
,渲染
if
else
之间的值,否则渲染
else
endif
之间的值。

视图函数中处理表单

@app.route('/',methods=['GET','POST'])
def index():
name = None
form = NameForm() #实例化表单类
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('index.html',form=form,name=name)


实测在wtf这里,需要用到前面的CSRF,即下面这个代码:

app.config['SECRET_KEY'] = 'hard to guess string'


如果不写到hello.py的话,就会有服务器端错误。

现在就可以打通简单的表单了,且运用Bootstrap为表单做格式渲染。

流程总结

浏览器地址栏输入地址,通过路由匹配到视图函数,视图函数实例化表单类,将其传递给模板(渲染模板时通过参数传递),于是在模板中,通过Jinja2引擎处理显示的逻辑。

本例是将表单实例和表单中的名字数据传入模板,当点击提交按钮后,会根据输入的值显示相应的内容。

form.validate_on_submit()
是提交按钮是否被点击的flag.

用户第一次访问程序,服务器收到没有表单数据的GET请求,if语句被跳过,浏览器端看到的是一个空表单。

提交表单后,服务器收到一个包含数据的POST请求。

重定向和用户会话

刷新页面会收到一个警告,出现这个警告的原因是:浏览器会重新发送之前已经发送过的最后一个请求。如果这个请求是包含表单数据的POST请求,刷新页面后会再次提交表单。

– 大多数情况下,这不是理想的处理方式

所以,处理方式是:不让Web程序把POST请求作为浏览器发送的最后一个请求。

实现方式:用重定向作为POST请求的响应,不使用常规响应。

重定向:特殊响应,响应内容是URL

当浏览器收到重定向的URL时,会发起GET请求,显示页面内容。也就是再走URL->视图函数->模板渲染这个流程。

所以,这种解决技巧也叫作 :
POST/重定向/GET
模式。

带来的问题:处理POST请求,使用
form.name.data
获取用户输入,一旦这个请求结束,数据就丢失了。所以,程序需要保存输入的名字

引出
session
,用户会话。

默认情况下,用户会话保存在客户端cookie中,使用
SECRET_KEY
进行加密签名。篡改了cookie中的内容,签名就会失效,会话也随之失效。

#注意要import的模块是:session, redirect, url_for
@app.route('/',methods=['GET','POST'])
def index():
form = NameForm() #实例化表单类
if form.validate_on_submit():
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html',form=form,name=session.get('name'))


这样就可以解决比较annoying的刷新时弹出确认提交的对话框。所以没这么干的网站还可以再进一步优化使用体验。

重定向文本可以写作:
redirect('/')
然后调用index视图函数,但是使用url_for(视图函数名)写法,可以保证修改路由名字后依然可用。

session的用法就是标准的字典用法。

session['xx'] = 'xxxx' #写入
session.get('xx')#读取


Flash消息

核心特性:请求完成后,让用户知道状态发生了变化。确认,警告或者错误题型。

实现方式:
flash()
函数。

#hello.py
from flask import Flask, render_template,session, redirect,url_for,flash
@app.route('/',methods=['GET','POST'])
def index():
form = NameForm() #实例化表单类
if form.validate_on_submit():
old_name = session.get('name')
if old_name is not None and old_name != form.name.data:
flash("Name changed!") #flash函数调用
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html',form=form,name=session.get('name'))


单独修改视图函数并不能显示flash消息。

需要在模板中渲染显示消息,且为了复用,可以在基模板中渲染Flash消息。

get_flashed_messages()
函数可以在模板中调用,此函数将会获得视图函数中的所有flash消息。

<!--base.html-->
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert"> ×</button>
{{ message }}
</div>
{% endfor %}


一定要注意
{% endfor %}
不要丢了。

至此,Flask中使用表单的基础知识大概点到了,具体在项目中用到时,参考本篇即可。

—笔记学习自狗书.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  web表单 Flask WTF