本文学习来源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
{% endblock %}
首先指明此模板继承于基类模板,在form中定义了三个属性,action 指向了某个真实服务器的临时 URL ,置空则表示当前页面,而在method属性里面指定值为POST
,而有时候我们使用的方法为GET
我们看一下POST
和GET
方法区别;
使用GET
会使得提交的内容在URL中可见,如果使用GET
方法提交表单,会使密码等泄露,而POST
通常用于提交表单和敏感数据,更多的信息可以参考此网站
而对于form.hidden_tag()
使得跨站请求伪造保护得到了支持。它的模板参数生成了一个隐藏字段,其中包含一个用于保护表单免受CSRF攻击的token
。在Flask-WTF文档中写到:
Form 类有一个 hidden_tag 方法, 它在一个隐藏的 DIV 标签中渲染任何隐藏的字段
{{ form.
,{{ 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
模板中:
接下来运行一下应用试一试吧!本机运行结果:
接收表单数据
当我们在页面中进行提交的时候,会发现有错误,回顾之前我们只进行了显示表单的工作,接下来我们通过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()
,当我们进行提交时它主要完成了两件事:
- 通过is_submitted()通过判断HTTP方法来确认是否提交了表单
- 通过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 %}
{% 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
{% endblock %}
相比之前,多加了一个for
循环来渲染错误信息,让其用红色字体显示出来,运行结果:
登录表单已经差不多完善了,下面让我们看看包含链接的更好方法。
生成链接
在之前我们在base.html
和routes.py
中包含了一些链接的例子:
return redirect('/index')
flask模块提供了url_for()
函数用于获取函数的URL
,因此在项目中所有引用到URL字符串的地方,都可以使用url_for(func, **kwargs)
来获取函数的URL,这样做的好处在于,可以使项目更容易维护。当某函数URL发生变更时,只需修改一处地方即可,而无须修改每一处URL引用,相比硬编码更加方便和易于维护,让我们使用url_for
来生成链接。
结果上述代码可改为如下:
在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:如果登陆了会重定向到欢迎界面),效果如下,谢谢您的观看。