《Flasky Web开发四》Web表单

request.form能获取POST请求中提交的表单数据,但是为了某些重复操作,例如生成表单的HTML代码和验证提交的表单数据,使用Flask-WTF扩展。

       pip install flask-wtf

4.1 跨站请求伪造保护

默认下,Flask-WTF能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery, CSRF)攻击。恶意网站把请求发送到被攻击者已登录的其他网站使就会引发CSRF攻击。

为了实现CSRF保护,Flask-WTF需要程序设置一个密钥,使用密钥生成加密令牌,再用令牌验证请求中表达数据的真伪。

app = Flask(__name__)

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

此处app.config字典可以用来存储框架、扩展和程序本身的配置变量。SECRET_KEY配置变量是通用密钥,可在Flask和多个第三方扩展中使用。注意:为了安全性,密钥不要直接写入代码,而是存在环境变量。

4.2 表单类

       使用Flask-WTF时,每个Web表单都由一个继承自Form的类表示。这个类定义表单中的一组字段,每个字段都用对象表示。字段对象可附属一个或多个验证函数。验证函数用来验证用户提交的输入值是否符合要求。

       from flask_wtf import Form  //flask.ext.wtf转换成flask_wtf

from wtforms import StringField, SubmitField

from wtforms.validators import Required

 

class NameForm(Form):

        name = StringField('Whst is your name?', validators = [Required()])

        submit = SubmitField('Submit')

       此处代码为一个简单的Web表单,包含一个文本字段和一个提交按钮。NameForm表单中有一个名为name的文本字段和一个名为submit的提交按钮。StringField类表示属性为type=”text”的<input>元素,SubmitField类表示属性为type=”submit”的<input>元素。StringField构造函数的可选参数validators指定一个由验证函数组成的列表,在接受用户提交的数据之前验证数据。验证函数Required()确保提交字段不为空。

       Form基类由Flask-WTF扩展定义,所以从flask_wtf导入。字段和验证函数直接从WTForms包中导入。

       WTForms支持的HTML标准字段和验证函数自行查阅。

4.3 把表单渲染成HTML

       视图函数把一个NameForm实例通过参数form传入模板,在模板中生成表单

       <form method=”POST”>

              {{ form.hidden_tag() }}

              {{ form.name.label }} {{ form.name() }}

              {{ form.submit() }}

       </form>

       可以为字段制定id或class属性,然后定义CSS样式

              {{ form.name.label }} {{ form.name(id=’my-text-field’) }}

       这种方式渲染表单的工作量较大,最好使用Bootstrap的表单样式

       {% import “boostrap/wtf.html” as wtf %}

       {{ wtf.quick_form(form) }}

4.4 在视图函数中处理表单

       GET - 从指定的资源请求数据

POST - 向指定的资源提交要被处理的数据

       @app.route('/', method=['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)

       此处将表单提交作为POST请求处理更加便利。如果GET请求提交表单,由于GET请求没有主体,数据会以查询字符串的形式附加到URL中。

       用户第一次访问程序时,服务器收到一个没有表单数据的GET请求,所以if语句部分跳过,通过渲染模板处理请求,并传入表单对象和值为None的name变量作为参数,用户在浏览器上看到一个表单。

      用户提交表单后,服务器收到一个包含数据的POST请求。validate_on_submit()调用name字段上附属的Required()验证函数,如果名字不为空则验证通过,validate_on_submit()返回True。if语句中,name获得名字,data属性设为空字符串从而清空表单字段。最后调用render_template()函数渲染模板,参数name的值为表单中输入的名字。

hello.py

       from flask import Flask, render_template

from flask_bootstrap import Bootstrap

from flask_moment import Moment

from flask_wtf import FlaskForm

from wtforms import StringField, SubmitField

from wtforms.validators import DataRequired

 

app = Flask(__name__)

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

 

bootstrap = Bootstrap(app)

moment = Moment(app)

 

class NameForm(FlaskForm):

    name = StringField('What is your name?', validators=[DataRequired()])

    submit = SubmitField('Submit')

 

@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)

 

app.run(host='0.0.0.0',debug=True)

index.html

       {% 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 %}

4.5 重定向和用户会话

       可用性问题:用户输入名字后提交表单,点击浏览器刷新按钮,会有再次提交表单警告。这是因为刷新页面时,浏览器会重新发送之前已经发送过的最后一个请求,而如果这个请求包含表单数据的POST请求,刷新页面会再次提交表单。

       解决办法:POST/重定向/GET模式

       使用重定向作为POST请求的响应,响应内容为URL而不是常规的包含HTML代码的字符串。浏览器收到重定向响应,会向重定向的URL发起GET请求,显示页面内容。而POST请求的form.name.data获取的输入名字将存储在用户会话中。

用户会话是一种私有存储,存在每个连接到服务器的客户端中,默认存在客户端的cookie中。

       from flask import Flask, render_template, session, redirect, url_for

from flask_bootstrap import Bootstrap

from flask_moment import Moment

from flask_wtf import FlaskForm

from wtforms import StringField, SubmitField

from wtforms.validators import DataRequired

 

app = Flask(__name__)

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

 

bootstrap = Bootstrap(app)

moment = Moment(app)

 

class NameForm(FlaskForm):

    name = StringField('What is your name?', validators=[DataRequired()])

    submit = SubmitField('Submit')

 

@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'))

       此处代码,原本的局部变量name现在保存在用户会话session[‘name’]中,所以两次请求之间能记住输入的值。合法表单数据的请求调用redirect()函数,参数为重定向的地址。url_for()默认唯一必须参数是路由的内部名字,默认为视图函数的名字,这里指index()。render_template()函数使用session.get(‘name’)直接从会话中读取name参数。get()获取字典中键对应的值,若无返回none。

4.6 Flask消息

       请求完成后,响应消息提示用户输入是否正确。使用flash()函数,在发给客户端的下一个响应中显示一个消息。

hello.py

       @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('Looks like you have changed your name!')

        session['name'] = form.name.data

        return redirect(url_for('index'))

return render_template('index.html', form=form, name=session.get('name'))

       每次提交的名字都会和存储在用户会话中的名字作比较,如果两个名字不同调用flash()函数,在发给客户端的下一个响应中显示一个消息。

base.html(建议在基模板中渲染)

       {% block content %}

<div class="container">

    {% for message in get_flashed_messages() %}

    <div class="alert alert-warning">

        <button type="button" class="close" data-dismiss="alert">&times;</button>

        {{ message }}

    </div>

    {% endfor %}

 

    {% block page_content %}{% endblock %}

</div>

{% endblock %}

       此处代码使用了for循环,因为在之前的请求循环中,每次调用flash()都会生成一个消息,所以可能有多个消息在排队等待显示。get_flashed_messages()函数获取的消息在下次调用时不会再次返回,因此Flash消息只显示一次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值