Python Web Flask — 如何透過Form取得資料

Sean Yeh
Python Everywhere -from Beginner to Advanced
16 min readNov 4, 2020

我們之前提過,在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頁面,看看是否仍然是已經登入的狀態。如果是,等待五分鐘看看結果如何?

--

--

Sean Yeh
Python Everywhere -from Beginner to Advanced

# Taipei, Internet Digital Advertising,透過寫作讓我們回想過去、理解現在並思考未來。並樂於分享,這才是最大贏家。