Python Web Flask — 如何透過Form取得資料
我們之前提過,在Flask裡面傳送資料的方式有GET與POST兩種。其中,Flask的路由預設使用GET方式進行路徑存取。而透過POST方式傳送的資料不會顯示在網址列,相對於以GET傳送資料來的安全。要使用POST方式傳送資料的話,我們會利用表單來輸入資料。今天我們要使用POST方式並且使用session,來製作login與logout功能。
POST表單
建立表單
我們要製作一個登入表單。首先匯入以下套件(Flask、redirect、url_for、render_template、request):
from flask import Flask, redirect, url_for, render_template,request
接著,我們建立一個login函式對應login路由,並在route中宣告使用POST與GET兩種HTTP存取方式,對應的HTML樣板則是login.html:
@app.route("/login", methods=["POST","GET"])def login(): return render_template("login.html")
建立一個user函式與路由:
@app.route("/<user>")
def user(user):
return render_template("user.html",user=user)
user函式對應到user.html樣板,樣板中有個user參數(<h1>Hello {{usr}}</h1>),該參數透過路由提供。
建立HTML樣板,樣板裡面放form表單,使用POST方法。
<form action="#" method="post"> <div class="form-group"> <label for="username">Name:</label> <input id="username" class="form-control" type="text" name="nm" /> </div> <div class="form-group"> <button type="submit" class="btn btn-primary">Submit</button> </div></form>
完整的HTML(login.html)部分原始碼如下:
{% extends "base.html" %}{% block title %}登入{% endblock %}
{% block body %}<form action="#" method="post"> <div class="form-group"> <label for="username">Name:</label> <input id="username" class="form-control" type="text" name="nm" /> </div> <div class="form-group"> <button type="submit" class="btn btn-primary">Submit</button> </div></form>{% endblock %}
執行後的畫面如下:
傳遞表單資料
建立表單後,我們要取得使用者在表單上輸入的資料,因此我們需要使用request.form[“nm”]的方式獲得使用者在表單上輸入的資料。為了達到這個目的,我們在login函式中加上一個if…else…判斷:如果使用者透過POST傳遞資料時,就去抓取form表單中name為nm的值,並且指定給變數user後,將網頁導向user頁面,並且將user的值帶入該頁面;否則就直接解析login.html樣版並呈現給使用者。
if request.method == "POST": user = request.form["nm"] return redirect(url_for("user",user=user))else: return render_template("login.html")
以下是到目前為止Python 部分的原始碼:
from flask import Flask, redirect, url_for, render_template,requestapp = Flask(__name__)
#login page@app.route("/login", methods=["POST","GET"])def login(): if request.method == "POST": user = request.form["nm"] return redirect(url_for("user",usr=user)) else: return render_template("login.html")#user page@app.route("/<usr>")def user(user):
return render_template("user.html",user=user)if __name__ =="__main__": app.run(debug=True)
輸入後的結果如下,網站會從login(http://127.0.0.1:5000/login)頁面帶到user (http://127.0.0.1:5000/sean)頁面:
問題點
到目前為止,我們每次像要登入頁面時,一定要輸入一次帳號,如果換了頁之後再回來,也要再一次的輸入帳號。如果我們想要將輸入的資訊,帶到接下來的頁面,該如何處理?說實在的這樣子的設計很不方便,而且我們也不希望登入的帳號出現在網址列上面。以下我們就要使用session來解決這個問題。
解決方式:使用sessions
當我們登入一個網站服務時,session可以紀錄我們登入(login)的帳號資訊,讓我們在之後瀏覽這個網站的其他頁面可以繼續使用這個session資訊作為判斷。如此,我們不用每次進入一個頁面,就要重新再登入一次。當你離開這個網站,或者是登出(logout)這個網站服務時,session儲存的資訊也會跟著消失無蹤。另外,它不像cookie,session的資訊是儲存在伺服器端,相對來說會比較安全。
接下來我們要試著做一個登入頁面,當使用者登入後,session會紀錄登入者的名稱,並且引導使用者到其他的頁面。
匯入session套件
為了達成這個目的,我們要修改上面的程式碼login的部分,在login函式中加上session,實際上方法很簡單,首現要匯入session套件:
from flask import session
匯入的session套件可以與前面的其他套件寫在一行:
from flask import Flask, redirect, url_for, render_template, request, session
修改login函式
匯入套件後只要加上這一行:
session["user"] = user
目前login函式應該長得像下面這樣:
@app.route("/login", methods=["POST","GET"])def login(): if request.method == "POST": user = request.form["nm"] session["user"] = user return redirect(url_for("user")) else: return render_template("login.html")
由於session的型態是字典,我們可以指定一個名稱(user)為字典的key,並且將form表單而來的user值賦予它(user = request.form[“nm”] )
然後,我們要將資料傳給user頁面。
由於有了session,需要修改原來的 return redirect(url_for(“user”,usr=user)),改為只要轉址到user頁面,而不需要再透過原來的方式傳遞user值。
return redirect(url_for("user"))
修改user函式
在user函式中取得傳來的session資料:
首先我們先判斷是否從session中取得user這個key,如果已經取得到了再把user這個key的value取出來放到user變數中,最後將值回傳。反之,若沒有從session中取得user這個key,就表示使用者尚未登入,我們可以將使用者導引至login的頁面。因此我們將user函式改為:
@app.route("/user")def user(): if "user" in session: user = session["user"]
return render_template("user.html",user=user) else: return redirect(url_for("login"))
你可以發現,原本的路由被修改為@app.route("/user")
因為我們不需要再從GET把參數帶到user 頁面中,user函式本身也不需要再帶入參數。
設定secret_key
修改了login函式與user函式後,我們還需要加上一個secret_key的設定,否則程式執行後,出現下面的錯誤。
你可以自行設定key的內容。當然,這個key越複雜難記,越不容易被人模仿。
app.secret_key = "#230dec61-fee8-4ef2-a791-36f9e680c9fc"
修改後結果如下:
from flask import Flask, redirect, url_for, render_template, request, sessionapp = Flask(__name__)
app.secret_key = "#230dec61-fee8-4ef2-a791-36f9e680c9fc"#login
@app.route("/login", methods=["POST","GET"])
def login():
if request.method == "POST":
user = request.form["nm"]
session["user"] = user
return redirect(url_for("user"))
else:
return render_template("login.html")
@app.route("/user")
def user():
if "user" in session:
user = session["user"]
return render_template("user.html",user=user)
else:
return redirect(url_for("login"))
if __name__ =="__main__":app.run(debug=True)
登出Logout
在目前為止完成的程式碼下,想要關閉session讓它消滅的話,使用者必須手動關閉瀏覽器。若我們希望在不關閉瀏覽器的狀態下就消滅session的話該如何處理?
我們可以使用「登出」的方式來達成。首先建立一個logout的路由( @app.route(“/logout”) ),當使用者進入Logout頁面時,用pop將session裡面記錄的user清除,清除的方式如下:
session.pop("user", None)
此外,當程式清除session紀錄之後,隨即讓頁面導向login頁。
整個logout函式如下:
@app.route("/logout")def logout(): session.pop("user", None) return redirect(url_for("login"))
為了配合logout的使用,我們也需要修改一下login的部分,讓已經有session紀錄(session[“user”])的使用者,從login的路由,直接被導向user頁面。
if "user" in session:
return redirect(url_for("user"))
修改後的Login:
#login@app.route("/login", methods=["POST","GET"])def login(): if request.method == "POST":
user = request.form["nm"]
session["user"] = user
return redirect(url_for("user")) else: if "user" in session:
return redirect(url_for("user"))
return render_template("login.html")
全部程式碼:
from flask import Flask, redirect, url_for, render_template, request, sessionapp = Flask(__name__)
app.secret_key = "#230dec61-fee8-4ef2-a791-36f9e680c9fc"
#login@app.route("/login", methods=["POST","GET"])def login(): if request.method == "POST":
user = request.form["nm"]
session["user"] = user
return redirect(url_for("user")) else: if "user" in session: return redirect(url_for("user"))
return render_template("login.html")
@app.route("/user")def user(): if "user" in session:
user = session["user"]
return render_template("user.html",user=user) else: return redirect(url_for("login"))
#logout
@app.route("/logout")def logout(): session.pop("user", None)
return redirect(url_for("login"))
if __name__ =="__main__": app.run(debug=True)
讓session的效果持續一段時間
目前我們使用的session,只要關閉瀏覽器,session的記憶就會消失。有時候,我們並不希望session隨著瀏覽器的關閉而結束它的效力。有時候我們會希望session 不因為瀏覽器的關閉而消失並且效力可以持續一段特定的時間,該如何達成這個效果?
為了達成這個結果,我們首先需要匯入timedelta套件。timedelta套件可以計算時間,在這裡可以讓我們替session設定存續的期間。
from datetime import timedelta
匯入timedelta後,接著設定session的存續期間,你也可以以「分」(minutes)為單位來設定,目前設定session存續期間為5分鐘(minutes=3),你也可以以「天」(days)為單位來設定,例如:days=7。
app.permanent_session_lifetime = timedelta(minutes=3)
接著,要在login函式的地方加入下面這一行才會有效果:
session.permanent = True
以下是login的程式碼:
#login@app.route("/login", methods=["POST","GET"])def login(): if request.method == "POST": session.permanent = True user = request.form["nm"] session["user"] = user return redirect(url_for("user")) else: if "user" in session: return redirect(url_for("user")) return render_template("login.html")
再次啟動伺服器並瀏覽login頁面,測試看看是否成功。
你可以先登入您的姓名後關閉瀏覽器。接著,再打開一次瀏覽器瀏覽login頁面,看看是否仍然是已經登入的狀態。如果是,等待五分鐘看看結果如何?