Flask Web表單
上一節我們定義了一個簡單的模板,並看到了實際運行的效果,接下來我們來看一下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 %}
寫在最後
我們隊登錄的表單做了很多的改進,但是我們還沒有真正的登錄到系統中,真正的登錄我們需要後臺的數據庫的支持