Flask学习之路(三)之表单

本文学习来源The-Flask-Mega-Tutorial-zh,学习如何使用Web表单,再次表达对译者的感谢,正是因为他,才能学习到这么好的教程。本篇仅作为自己Flask入门的记录,想通过此来记录代码和自己不懂的概念。

Flask-WTF简介


让我们学习之前先了解下Flask-WTF插件,通过pip3 install Flask——WTF,在每次安装插件后都建议打开Python解释器测试是否安装成功,测试命令为>>>import flask_wtf,若接下来为>>>则表明模块导入成功,在WTForms的介绍中显示,WTForms可以生成表单字段的HTML,也可以在模板中自定义它,来实现代码和表单的分离,而Flask-WTF为WTForms提供了一个简单接口。

配置应用


在文中好几处提到了松耦合这个概念,在配置应用前让我们讲讲松耦合,它主要是为了模块的独立性,在维基百科的编程部分解释道:耦合是指一个组件直接了解其他组件的程度,Flask-WTF就是一个松耦合的例子。在接下来的应用配置中也会用到,先让我们看个例子,通过app.config来配置应用

my_app = Flask(__name__)
app.config['SECRET_KEY'] = 'you-will-never-guess'

这样使得配置和应用代码处于了一个文件,随着程序规模的增大,会越来越不利于维护,通过松耦合便可以解决这个问题。让我们在microblog/目录下新建config.py模块进行配置

import os

class Config(object):#python中所有的类都继承自object类
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'

在这里提供了一个os.environ是存放环境变量的一个类,通过get方法来获得SECRET_KEY的值,在这里os.environ是一个字典变量。而SECRET_KEY是用来对Flask的敏感部分进行加密。此处or为表达式,连接第一个项用来查找SECRET_KEY值,第二个是hardcode字符串,这样就提高了Flask应用的安全性能。
然后我们在__init__.py里面进行初始化,使得Flask来读取并使用config.py配置文件:

from flask import Flask
from config import Config

my_app = Flask(__name__)
my_app.config.from_object(Config)

from app import routes

在这里有一句代码是my_app.config.from_object(Config),在配置中使用类和继承我们需要调用from_object,可以参考技术手册
配置完成后,我们可以在交互环境中进行测试

>>> from microblog import my_app
>>> my_app.config['SECRET_KEY']

如果没有问题则会显示硬编码的值'you-will-never-guess',下面来看看用户登录表单的实现。

用户登录表单


Flask-WTF插件中,支持将表单字段定义为类属性,为了使结构更加明了和维护方便,我们使用app/forms.py来实现

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    remember_me = BooleanField('Remember Me')
    submit = SubmitField('Sign In')

在这里可以看出导入的FlaskForm作为LoginForm的父类,导入的StringField、PasswordField,分别有着参数Username、Password,后面的参数validators则表明了验证规则,BooleanField则定义了一个Checkbox类型的,若加上default='checked'默认勾选此框,而SubmitField则创建了一个submit按钮,导入的DataRequired是用来进行验证必填项的,也就是不填User和password会产生报错,下面来看看表单模板。

表单模板


下面完成将表单添加进模板里,在templates下新建login.html:

{% extends "base.html" %} 

{% block content %}
    

Sign In

{{ form.hidden_tag() }}

{{ form.username.label }}
{{ form.username(size=32) }}

{{ form.password.label }}
{{ form.password(size=32) }}

{{ form.remember_me() }} {{ form.remember_me.label }}

{{ form.submit() }}

{% endblock %}

首先指明此模板继承于基类模板,在form中定义了三个属性,action 指向了某个真实服务器的临时 URL ,置空则表示当前页面,而在method属性里面指定值为POST,而有时候我们使用的方法为GET我们看一下POSTGET方法区别;
使用GET会使得提交的内容在URL中可见,如果使用GET方法提交表单,会使密码等泄露,而POST通常用于提交表单和敏感数据,更多的信息可以参考此网站
而对于form.hidden_tag()使得跨站请求伪造保护得到了支持。它的模板参数生成了一个隐藏字段,其中包含一个用于保护表单免受CSRF攻击的token。在Flask-WTF文档中写到:

Form 类有一个 hidden_tag 方法, 它在一个隐藏的 DIV 标签中渲染任何隐藏的字段

{{ form..label }}{{ form.() }}则是为了指明其需要在渲染时转化为HTML元素 及其类别,下面编写表单视图来渲染模板

表单视图


让我们在app/routes.py模块中编写这个视图函数:

from flask import render_template
from app import my_app
from app.forms import LoginForm

#代码中间部分

@app.route('/login')
def login():
    form = LoginForm()
    return render_template('login.html', title='Sign In', form=form)#render_templatedi第一个参数传入页面,然后根据后面的参数渲染模板

然后把登录的链接添加到导航栏,就是base.html模板中:

Microblog: Home Login

接下来运行一下应用试一试吧!本机运行结果:
Flask学习之路(三)之表单_第1张图片

接收表单数据


当我们在页面中进行提交的时候,会发现有错误,回顾之前我们只进行了显示表单的工作,接下来我们通过Flask-WTF对提交的数据进行处理和验证。

from flask import render_template, flash, redirect
#关于GET、POST属性有遗忘的话可以查看前面的介绍
@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        flash('Login requested for user {}, remember_me={}'.format(
            form.username.data, form.remember_me.data))
        return redirect('/index')
    return render_template('login.html', title='Sign In', form=form)

接下来让我们看下form.validate_on_submit(),当我们进行提交时它主要完成了两件事:

  1. 通过is_submitted()通过判断HTTP方法来确认是否提交了表单
  2. 通过WTForms提供的validate()来验证表单数据(使用我们在下面的表单类里给每个字段传入的验证函数)
    在stack overflow找到了自己可理解的解释:

validate_on_submit() is a shortcut for is_submitted() and validate().
Generally speaking, it is used when a route can accept both GET and POST methods and you want to validate only on a POST request.

验证通过后会返回True,要是没通过则会像之前一样返回GET请求的渲染页面。
flash是为了向用户反馈,比如在用户登录成功后显示消息,以使得应用对用户更加友好易用。
下面来看看redirect(),可以观察到其有一个URL参数,通过英文意思我们可以猜测,其是在验证通过后返回/index页面,函数的实际功能也是这样。
当调用flash()函数后,FLASk会储存这个消息,为了让其显示在页面上,需要将消息渲染到基础模板中,更新后的base.html如下:


    
        {% if title %}
        {{ title }} - microblog
        {% else %}
        microblog
        {% endif %}
    
    
        
Microblog: Home Login

{% with messages = get_flashed_messages() %} {% if messages %}
    {% for message in messages %}
  • {{ message }}
  • {% endfor %}
{% endif %} {% endwith %} {% block content %}{% endblock %}

with结构把get_flashed_messages()的结果赋值给变量messages,前者返回flash()注册过的消息列表,接下来判断并遍历渲染消息列表,然而在运行应用的过程中出现了一些问题,错误代码中出现:

__init__() takes from 1 to 2 positional arguments but 3 were given

从错误提示中发现是验证器出现了问题,最后在stackoverflow找到了解决方案,在自己编写过程form.py中,validators=[DataRequired()]丢失了()。若编写正确,置空点击Sign In后应当刷新Sign In页面。

完善字段验证


表单验证是为了防止提交无效数据,若接收到无效表单,则应该重新显示表单,让用户输入合法数据,在之前的设计中我们没有给出足够的错误反馈,现在让我们完善这个功能。(注:其实错误已经生成,下面通过逻辑来渲染它们)
让我们在login.html中为username和password字段添加验证描述性错误消息渲染逻辑:

{% extends "base.html" %}

{% block content %}
    

Sign In

{{ form.hidden_tag() }}

{{ form.username.label }}
{{ form.username(size=32) }}
{% for error in form.username.errors %} [{{ error }}] {% endfor %}

{{ form.password.label }}
{{ form.password(size=32) }}
{% for error in form.password.errors %} [{{ error }}] {% endfor %}

{{ form.remember_me() }} {{ form.remember_me.label }}

{{ form.submit() }}

{% endblock %}

相比之前,多加了一个for循环来渲染错误信息,让其用红色字体显示出来,运行结果:

Flask学习之路(三)之表单_第2张图片

登录表单已经差不多完善了,下面让我们看看包含链接的更好方法。

生成链接


在之前我们在base.htmlroutes.py中包含了一些链接的例子:


Microblog: Home Login
return redirect('/index')

flask模块提供了url_for()函数用于获取函数的URL,因此在项目中所有引用到URL字符串的地方,都可以使用url_for(func, **kwargs)来获取函数的URL,这样做的好处在于,可以使项目更容易维护。当某函数URL发生变更时,只需修改一处地方即可,而无须修改每一处URL引用,相比硬编码更加方便和易于维护,让我们使用url_for来生成链接。
结果上述代码可改为如下:


Microblog: Home Login

routes.py中的login中也要进行修改,不过先得引入url_for模块。

from flask import render_template, flash, redirect, url_for
#中间代码部分
@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        # ...
        return redirect(url_for('index'))#修改此句
    # ...

在此表单差不多就完成了,(Ps:如果登陆了会重定向到欢迎界面),效果如下,谢谢您的观看。

Flask学习之路(三)之表单_第3张图片

你可能感兴趣的:(Flask学习之路(三)之表单)