Flask开发环境配置
Flask快速入门
Flask实践Step by Step -- 'Hello World'
Flask实践Step by Step -- 模板
Flask实践Step by Step -- Web表单
Flask Web Forms
上一节我们定义了一个简单的模板,并看到了实际运行的效果,接下来我们来看一下Web表单是如何
工作的,Web表单是Web应用程序的基础控件,使用表单可以使用用户写博客,进行登录等
配置
为了使用表单,我们需要使用Flask的一个扩展插件 Flask-WTF 需要先安装 pip install flask-wtf
,
许多Flask扩展需要很多的配置,我需要一个配置文件来进行管理 config.py
WTF_CSRF_ENABLED = True
SECRET_KEY = 'you-will-never-guess'
配置文件中的内容非常简单,需要两项设置即可,WTF_CSRF_ENABLED
设置会激活跨站访问保护
这个设置在这个版本中Flask-WTF是默认打开的,为了更安全我们这里还是显示的设置为 True
SECRECT_KEY
的设置只有当WTF_CSRF_ENABLED
为true时才会需要,为表单验证创建一个密码
token,这个key值尽量设置的复杂一些
接下来就需要Flask读取相关的配置,并且使用这些配置,在Flask的应用程序创建之后(file app/__init__.py
)
from flask import Flask
app = Flask(__name__)
app.config.from_object('config')
from app import views
用户登录表单
Flask-WTF的表单是一个集成基类Form
的一个类,现在我们需要创建一个登录的表单,需要用到身份认证系统
登录机制不是标准的 用户名/密码 模式,我们在这里引入了 OpendID,OpenID本身已经提供了
很好的验证,我们就不在需要验证密码了,这样可以使网站更安全,需要安装OpenID pip install flask-openid
使用OpenID登录只需要一个字符串,我们还提供了一个 'remember me' 选择框,用户可以选择是否需要浏览器在cookie中记住
用户的选择
写第一个表单 (file app/forms.py
)
from flask.ext.wtf import Form
from wtforms import StringField,BooleanField
from wtforms.validators import DataRequired
class LoginForm(Form):
openid = StringField('openid',validators=[DataRequired()])
remember_me = BooleanField('remember_me',default=False)
很简单的一个类,继承Form
两个字段,StringField
和BooleanField
,DataRequired
是一个验证器
一个方法和一个字段绑定,这个DataRequired
验证器只是简单的验证提交的输入内容,这Flask-WTF中还有很多
验证器,后续会继续介绍
表单模板
我们需要一个模板来生成HTML的表单,我们需要创建一个新的模板 (file app/templates/login.html
)
{% extends "base.html" %}
{% block content %}
<h1>Sign in</h1>
<form class="" action="" name="login" method="post">
{{form.hidden_tag()}}
<p>
Please enter your OpenID:<br>
{{ form.openid(size=80) }}
</p>
<p>
{{ form.remember_me }} Remember Me
</p>
<p>
<input type="button" value="Sign in" type="submit">
</p>
</form>
{% endblock %}
这个模板中同样是继承base.html
,这个模板和普通的HTML页面的表单还是有一些区别的,需要
视图中对应的方法来血染这个模板,form.hidden_tag()
会被HTML中的隐藏的域来取代,这个参数
需要把CSRF设置为Enabled状态
表单视图
我们需要一个视图方法来渲染这个模板(file app/views.py
)
from flask import render_template,flash,redirect
from app import app
from .forms import LoginForm
#index view function...
@app.route('/login',methods=['GET','POST'])
def login():
form = LoginForm()
return render_template('login.html',
title="Sign in",
form=form)
需要引入 LoginForm
这类,然后实例化,将这个实例化对象作为参数传入到模板中,我们还引入了
flash
和redirect
,可以暂时忽略这两个引用,以后我们会用到,在装饰器中我们加入了 methods
作为参数,告诉Flask我们的这个方法接收 GET和POST请求,保存运行,打开浏览器 访问 http://127.0.0.1:5000/login
看一下实际的效果,暂时我们还没有处理接收数据的处理,所以现在点击提交按钮会没有任何反馈
接收数据
Flask-WTF能够很容易处理从客户端发来的数据,我们需要修改一下 login的方法(file app/views.py
)
@app.route('/login',methods=['GET','POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
flash('Login requested for OpenID=""%s",remember_me=%s'%
(form.openid.data,str(form.remember_me.data)))
return redirect('/index')
return render_template('login.html',
title="Sign in",
form=form)
方法 validate_on_submit
会处理所有的表单的处理工作,表单展示给用户时会调用这个方法此时会返回 False
当提交请求时,这个方法会接收所有的数据,验证数据的合法性,如果验证全部正确,这个方法会返回True,只要有一个
参数验证不通过,这个方法就会返回 False,稍后我们会展示如何给用户展示错误信息,当 validate_on_submit
返回True
时,方法 flash
会展示一个快速的信息,在调到下一个页面之前,这个flash的信息不回显示在页面中,我们需要修改模板信息
来显示这个信息(file app/templates/base.html
)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
{% if title %}
<title>{{ title }} - microblog</title>
{% else %}
<title>Welcome to microblog</title>
{% endif %}
</head>
<body>
<div>
Microblog : <a href="/index">Home</a>
</div>
<hr>
{% with messages = get_flashed_messages()%}
{% if messages %}
<ul>
{% for msg in messages %}
<li>{{ msg }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</body>
</html>
方法get_flashed_messages
会接收flash
方法发送来的消息,并展示出来,在视图方法中我们还使用另一个
方法 redirect
,这个方法会重定向到另一网页地址
改进数据的验证
当用户的输入的信息有误时,希望能够提供给用户友好的错误的信息提示,我们修改一下login的模板(file app/templates/login.html
)
{% extends "base.html" %}
{% block content %}
<h1>Sign in</h1>
<form class="" action="" name="login" method="post">
{{form.hidden_tag()}}
<p>
Please enter your OpenID:<br>
{{ form.openid(size=80) }}
{% for error in form.openid.errors %}
<span style="color:red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.remember_me }} Remember Me
</p>
<p>
<input value="Sign in" type="submit">
</p>
</form>
{% endblock %}
添加了错误的提示信息
处理OpenIDs
为了是用户更容易的登录网站,我们添加一些openid的连接,儿不需要用户手动的输入OpenID
先定义一些OpenID的提供者,定义在config.py
中
WTF_CSRF_ENABLED = True
SECRET_KEY = 'you-will-never-guess'
OPENID_PROVIDERS = [
{'name': 'Google', 'url': 'https://www.google.com/accounts/o8/id'},
{'name': 'Yahoo', 'url': 'https://me.yahoo.com'},
{'name': 'AOL', 'url': 'http://openid.aol.com/<username>'},
{'name': 'Flickr', 'url': 'http://www.flickr.com/<username>'},
{'name': 'MyOpenID', 'url': 'https://www.myopenid.com'}]
然后在视图方法中使用
@app.route('/login',methods=['GET','POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
flash('Login requested for OpenID=""%s",remember_me=%s'%
(form.openid.data,str(form.remember_me.data)))
return redirect('/index')
return render_template('login.html',
title="Sign in",
form=form,
providers=app.config['OPENID_PROVIDERS'])
还需要修改对应的模板视图(file app/templates/login.html
)
{% extends "base.html" %}
{% block content %}
<script type="text/javascript">
function set_openid(openid,pr){
u = openid.search('<username>')
if(u != -1){
user = prompt('Enter your '+ pr + 'username')
openid = openid.substr(0,u)+user
}
form = document.forms['login']
form.elements['openid'].value = openid
}
</script>
<h1>Sign in</h1>
<form class="" action="" name="login" method="post">
{{form.hidden_tag()}}
<p>
Please enter your OpenID:<br>
{{ form.openid(size=80) }}
{% for error in form.openid.errors %}
<span style="color:red;">[{{ error }}]</span>
{% endfor %}<br>
| {% for pr in providers %}
<a href="javascript:set_openid('{{ pr.url }}','{{pr.name}}')">{{ pr.name }}</a> |
{% endfor %}
</p>
<p>
{{ form.remember_me }} Remember Me
</p>
<p>
<input value="Sign in" type="submit">
</p>
</form>
{% endblock %}
写在最后
我们队登录的表单做了很多的改进,但是我们还没有真正的登录到系统中,真正的登录我们需要后台的数据库的支持