Flask學(xué)習(xí)之路(三)之表單

本文學(xué)習(xí)來源The-Flask-Mega-Tutorial-zh,學(xué)習(xí)如何使用Web表單网梢,再次表達對譯者的感謝履澳,正是因為他,才能學(xué)習(xí)到這么好的教程哭廉。本篇僅作為自己Flask入門的記錄畜普,想通過此來記錄代碼和自己不懂的概念。

Flask-WTF簡介


讓我們學(xué)習(xí)之前先了解下Flask-WTF插件,通過pip3 install Flask——WTF群叶,在每次安裝插件后都建議打開Python解釋器測試是否安裝成功吃挑,測試命令為>>>import flask_wtf,若接下來為>>>則表明模塊導(dǎo)入成功,在WTForms的介紹中顯示街立,WTForms可以生成表單字段的HTML舶衬,也可以在模板中自定義它,來實現(xiàn)代碼和表單的分離赎离,而Flask-WTF為WTForms提供了一個簡單接口逛犹。

配置應(yīng)用


在文中好幾處提到了松耦合這個概念,在配置應(yīng)用前讓我們講講松耦合,它主要是為了模塊的獨立性,在維基百科的編程部分解釋道:耦合是指一個組件直接了解其他組件的程度,Flask-WTF就是一個松耦合的例子虽画。在接下來的應(yīng)用配置中也會用到舞蔽,先讓我們看個例子,通過app.config來配置應(yīng)用

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

這樣使得配置和應(yīng)用代碼處于了一個文件码撰,隨著程序規(guī)模的增大渗柿,會越來越不利于維護,通過松耦合便可以解決這個問題脖岛。讓我們在microblog/目錄下新建config.py模塊進行配置

import os

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

在這里提供了一個os.environ是存放環(huán)境變量的一個類朵栖,通過get方法來獲得SECRET_KEY的值,在這里os.environ是一個字典變量柴梆。而SECRET_KEY是用來對Flask的敏感部分進行加密陨溅。此處or為表達式,連接第一個項用來查找SECRET_KEY值绍在,第二個是hardcode字符串门扇,這樣就提高了Flask應(yīng)用的安全性能。
然后我們在__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)臼寄,在配置中使用類和繼承我們需要調(diào)用from_object,可以參考技術(shù)手冊
配置完成后卸察,我們可以在交互環(huán)境中進行測試

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

如果沒有問題則會顯示硬編碼的值'you-will-never-guess'脯厨,下面來看看用戶登錄表單的實現(xiàn)。

用戶登錄表單


Flask-WTF插件中坑质,支持將表單字段定義為類屬性合武,為了使結(jié)構(gòu)更加明了和維護方便,我們使用app/forms.py來實現(xiàn)

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

在這里可以看出導(dǎo)入的FlaskForm作為LoginForm的父類涡扼,導(dǎo)入的StringField稼跳、PasswordField,分別有著參數(shù)Username、Password吃沪,后面的參數(shù)validators則表明了驗證規(guī)則汤善,BooleanField則定義了一個Checkbox類型的,若加上default='checked'默認勾選此框票彪,而SubmitField則創(chuàng)建了一個submit按鈕红淡,導(dǎo)入的DataRequired是用來進行驗證必填項的,也就是不填User和password會產(chǎn)生報錯降铸,下面來看看表單模板在旱。

表單模板


下面完成將表單添加進模板里,在templates下新建login.html:

{% extends "base.html" %} 

{% block content %}
    <h1>Sign In</h1>
    <form action="" method="post" novalidate>
        {{ form.hidden_tag() }}
        <p>
            {{ form.username.label }}<br>
            {{ form.username(size=32) }}
        </p>
        <p>
            {{ form.password.label }}<br>
            {{ form.password(size=32) }}
        </p>
        <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
        <p>{{ form.submit() }}</p>
    </form>
{% endblock %}

首先指明此模板繼承于基類模板推掸,在form中定義了三個屬性桶蝎,action 指向了某個真實服務(wù)器的臨時 URL 驻仅,置空則表示當(dāng)前頁面,而在method屬性里面指定值為POST登渣,而有時候我們使用的方法為GET我們看一下POSTGET方法區(qū)別噪服;
使用GET會使得提交的內(nèi)容在URL中可見,如果使用GET方法提交表單胜茧,會使密碼等泄露粘优,而POST通常用于提交表單和敏感數(shù)據(jù),更多的信息可以參考此網(wǎng)站
而對于form.hidden_tag()使得跨站請求偽造保護得到了支持竹揍。它的模板參數(shù)生成了一個隱藏字段敬飒,其中包含一個用于保護表單免受CSRF攻擊的token邪铲。在Flask-WTF文檔中寫到:

Form 類有一個 hidden_tag 方法芬位, 它在一個隱藏的 DIV 標簽中渲染任何隱藏的字段

{{ form.<field_name>.label }}{{ form.<field_name>() }}則是為了指明其需要在渲染時轉(zhuǎn)化為HTML元素 及其類別带到,下面編寫表單視圖來渲染模板

表單視圖


讓我們在app/routes.py模塊中編寫這個視圖函數(shù):

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第一個參數(shù)傳入頁面昧碉,然后根據(jù)后面的參數(shù)渲染模板

然后把登錄的鏈接添加到導(dǎo)航欄,就是base.html模板中:

<div>
    Microblog:
    <a href="/index">Home</a>
    <a href="/login">Login</a>
</div>

接下來運行一下應(yīng)用試一試吧揽惹!本機運行結(jié)果:

接收表單數(shù)據(jù)


當(dāng)我們在頁面中進行提交的時候被饿,會發(fā)現(xiàn)有錯誤,回顧之前我們只進行了顯示表單的工作搪搏,接下來我們通過Flask-WTF對提交的數(shù)據(jù)進行處理和驗證狭握。

from flask import render_template, flash, redirect
#關(guān)于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()疯溺,當(dāng)我們進行提交時它主要完成了兩件事:

  1. 通過is_submitted()通過判斷HTTP方法來確認是否提交了表單
  2. 通過WTForms提供的validate()來驗證表單數(shù)據(jù)(使用我們在下面的表單類里給每個字段傳入的驗證函數(shù))
    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是為了向用戶反饋囱嫩,比如在用戶登錄成功后顯示消息恃疯,以使得應(yīng)用對用戶更加友好易用。
下面來看看redirect(),可以觀察到其有一個URL參數(shù)墨闲,通過英文意思我們可以猜測今妄,其是在驗證通過后返回/index頁面,函數(shù)的實際功能也是這樣鸳碧。
當(dāng)調(diào)用flash()函數(shù)后盾鳞,F(xiàn)LASk會儲存這個消息,為了讓其顯示在頁面上瞻离,需要將消息渲染到基礎(chǔ)模板中腾仅,更新后的base.html如下:

<html>
    <head>
        {% if title %}
        <title>{{ title }} - microblog</title>
        {% else %}
        <title>microblog</title>
        {% endif %}
    </head>
    <body>
        <div>
            Microblog:
            <a href="/index">Home</a>
            <a href="/login">Login</a>
        </div>
        <hr>
        {% with messages = get_flashed_messages() %}
        {% if messages %}
        <ul>
            {% for message in messages %}
            <li>{{ message }}</li>
            {% endfor %}
        </ul>
        {% endif %}
        {% endwith %}
        {% block content %}{% endblock %}
    </body>
</html>

with結(jié)構(gòu)把get_flashed_messages()的結(jié)果賦值給變量messages,前者返回flash()注冊過的消息列表琐脏,接下來判斷并遍歷渲染消息列表攒砖,然而在運行應(yīng)用的過程中出現(xiàn)了一些問題缸兔,錯誤代碼中出現(xiàn):

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

從錯誤提示中發(fā)現(xiàn)是驗證器出現(xiàn)了問題,最后在stackoverflow找到了解決方案吹艇,在自己編寫過程form.py中惰蜜,validators=[DataRequired()]丟失了()。若編寫正確受神,置空點擊Sign In后應(yīng)當(dāng)刷新Sign In頁面抛猖。

完善字段驗證


表單驗證是為了防止提交無效數(shù)據(jù),若接收到無效表單鼻听,則應(yīng)該重新顯示表單财著,讓用戶輸入合法數(shù)據(jù),在之前的設(shè)計中我們沒有給出足夠的錯誤反饋撑碴,現(xiàn)在讓我們完善這個功能撑教。(注:其實錯誤已經(jīng)生成,下面通過邏輯來渲染它們)
讓我們在login.html中為username和password字段添加驗證描述性錯誤消息渲染邏輯:

{% extends "base.html" %}

{% block content %}
    <h1>Sign In</h1>
    <form action="" method="post" novalidate>
        {{ form.hidden_tag() }}
        <p>
            {{ form.username.label }}<br>
            {{ form.username(size=32) }}<br>
            {% for error in form.username.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>
            {{ form.password.label }}<br>
            {{ form.password(size=32) }}<br>
            {% for error in form.password.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
        <p>{{ form.submit() }}</p>
    </form>
{% endblock %}

相比之前醉拓,多加了一個for循環(huán)來渲染錯誤信息伟姐,讓其用紅色字體顯示出來,運行結(jié)果:


登錄表單已經(jīng)差不多完善了亿卤,下面讓我們看看包含鏈接的更好方法愤兵。

生成鏈接


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

<!--base.html中-->
<div>
        Microblog:
        <a href="/index">Home</a>
        <a href="/login">Login</a>
  </div>
 <!--routes.py中-->   
   return redirect('/index')

flask模塊提供了url_for()函數(shù)用于獲取函數(shù)的URL,因此在項目中所有引用到URL字符串的地方排吴,都可以使用url_for(func, **kwargs)來獲取函數(shù)的URL秆乳,這樣做的好處在于,可以使項目更容易維護钻哩。當(dāng)某函數(shù)URL發(fā)生變更時屹堰,只需修改一處地方即可,而無須修改每一處URL引用憋槐,相比硬編碼更加方便和易于維護双藕,讓我們使用url_for來生成鏈接。
結(jié)果上述代碼可改為如下:

<!--base.html中-->
<div>
        Microblog:
        <a href="/index">Home</a>
        <a href="/login">Login</a> 
</div>

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:如果登陸了會重定向到歡迎界面),效果如下近范,謝謝您的觀看嘶摊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市评矩,隨后出現(xiàn)的幾起案子叶堆,更是在濱河造成了極大的恐慌,老刑警劉巖斥杜,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虱颗,死亡現(xiàn)場離奇詭異沥匈,居然都是意外死亡,警方通過查閱死者的電腦和手機忘渔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門高帖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人畦粮,你說我怎么就攤上這事散址。” “怎么了宣赔?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵预麸,是天一觀的道長。 經(jīng)常有香客問我儒将,道長吏祸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任椅棺,我火速辦了婚禮犁罩,結(jié)果婚禮上齐蔽,老公的妹妹穿的比我還像新娘两疚。我一直安慰自己,他們只是感情好含滴,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布诱渤。 她就那樣靜靜地躺著,像睡著了一般谈况。 火紅的嫁衣襯著肌膚如雪勺美。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天碑韵,我揣著相機與錄音赡茸,去河邊找鬼。 笑死祝闻,一個胖子當(dāng)著我的面吹牛占卧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播联喘,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼华蜒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了豁遭?” 一聲冷哼從身側(cè)響起叭喜,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蓖谢,沒想到半個月后捂蕴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體譬涡,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年啥辨,在試婚紗的時候發(fā)現(xiàn)自己被綠了昂儒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡委可,死狀恐怖渊跋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情着倾,我是刑警寧澤拾酝,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站卡者,受9級特大地震影響蒿囤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜崇决,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一材诽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恒傻,春花似錦脸侥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至沸手,卻和暖如春外遇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背契吉。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工跳仿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捐晶。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓菲语,卻偏偏與公主長得像,于是被迫代替她去往敵國和親租悄。 傳聞我的和親對象是個殘疾皇子谨究,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容