總結
Flask Web 框架 輕量 websocket 全雙工通訊 socket TCP 通訊 MongoDB 資料庫 檔案型資料庫 {} 沒有限制 和 約束 Mui + HTML5 Plus 呼叫移動作業系統的封裝 IOS Android 人工智慧技術應用 BaiduAI ASR 語音識別 聲音轉換成文字 TTS 語音合成 文字轉換成聲音 NLP 自然語言處理 你的名字叫什麼 你的名字是什麼 文字相似度 paddle paddle 百度 Ai studio 智慧玩具 Flask 1.框架對比 Django Flask Admin - Model 原生無 Model 原生無 Form 原生無 Session 有 - 顛覆認知操作 教科書式框架 以簡單為基準 開發 一切從簡 能省則省 Django的優勢: 元件全,功能全,教科書 Django的劣勢: 佔用資源,建立複雜度較高 Flask的優勢: 輕,快 Flask的劣勢: 先天不足,第三方元件穩定性較差 2.入門Flask pip3 install Flask **Ps:不要使用工具中的外掛建立Flask專案 三行程式碼啟動 Flask 專案 3.Flask 中的 Response 1.HTTPResponse("hello") "" str 2.render 響應模板 render_template("html") str 3.redirect("/") redirect("/") str 以上是Web框架的 Response 三劍客 4.send_file() instance 返回檔案內容,自動識別檔案型別,Content-type中新增檔案型別,Content-type:檔案型別 ** 瀏覽器特性 可識別的Content-type 自動渲染 不可識別的Content-type 會自動下載 5.jsonify() str # 返回標準格式的JSON字串 先序列化JSON的字典,Content-type中加入 Application/json ** Flask 1.1.1 版本中 可以直接返回字典格式,無需jsonify 4.Flask 中的 請求 Request request.method 獲取請求方式 request.form 獲取FormData中的資料 也就是所謂的Form標籤 to_dict() request.args 獲取URL中的資料 to_dict() request.json 請求中 Content-Type:application/json 請求體中的資料 被序列化到 request.json 中 以字典的形式存放 request.data 請求中 Content-Type 中不包含 Form 或 FormData 保留請求體中的原始資料 b"" request.files 獲取Form中的檔案 request.path 請求路徑 路由地址 request.url 訪問請求的完整路徑包括 url引數 request.host 主機位 127.0.0.1:5000 request.cookies 字典獲取瀏覽器請求時帶上的Cookie ** request.values 獲取 url 和 FormData 中的資料 敏感地帶 5.Jinja2 ---- template語言 {{ }} 引用 或 執行 {% %} 邏輯引用
Flask 入門
1.框架對比 Django Admin 元件- 基於Model實現的,Model,Form。教科書式框架
Flask 無Admin 原生無 Model 原生無 Form Session 有 - 顛覆認知操作。flask雖然原生沒有,但是第三方元件特別多。只要有個人寫個擴充套件功能出來,直接放到flask中,flask就能用了。第三方元件多,強大。如果包括第三方元件,那麼flask比Django功能還要多。缺點:flask自己是不支援那麼多元件的,官方認可,但不代表是他們開發的,所以當flask版本升級了,有可能使得第三方元件用不了了。flask宗旨:以簡單為基準 開發 一切從簡 能省則省。這個session沒有摒棄,因為它佔有資源非常少
Django的優勢: 元件全,功能全,教科書 Django的劣勢: 佔用資源,建立(開發)複雜度較高
Flask的優勢: 輕,快 Flask的劣勢: 先天不足,第三方元件穩定性較差
2.入門Flask
pip3 install Flask **Ps:不要使用工具中的外掛建立Flask專案
三行程式碼啟動 Flask 專案
不要勾選繼承全域性包,不然會複製一份包過來,會很慢,直接建立新的。
安裝flask元件
解除安裝之後換個版本
Flask框架原始碼,Jinja2模板語言,Django裡template是基於Jinja2實現的。MarkupSafe是後端給前端傳遞字串,將字串轉為標籤來展示在瀏覽器中使用的。render底層就是基於它。Werkzeug ① 工具 ② (動物的某一)器官 ③ 工具(指人、機構等) .Werkzeug 相當於Django的uwsgi,承載服務用的,類似於tomcat,jboss等web服務,應用程式閘道器介面,它和uwsgi的底層是一樣的,都是wsgi。這些是flask的主要依賴包
建立一個flask專案。啟動專案。
from flask import Flask
app=Flask(__name__)
app.run()
成功訪問:
在去檢視訪問情況,可以看到後臺獲取/,但是資源沒有找到,這是應用層丟擲的錯誤。如果服務沒起就訪問,丟擲的就不是404了,比如超時等之類的錯誤
flask實現hello world
如果@app.route()每加路由,那麼報錯:TypeError: route() missing 1 required positional argument: 'rule'
from flask import Flask
app=Flask(__name__)
@app.route("/")
def home():
return "hello world I am mcw"
app.run()
/是路由地址,定義檢視函式,直接返回字串。可以在瀏覽器上顯示出返回的字串。下面我們就可以用這三行程式碼實現flask所有的功能。
訪問情況:
如果沒有返回內容,那麼會報錯500伺服器異常:
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
我們的檢視函式沒有返回值,那麼就是預設返回的None,即使我們給函式後面新增一個return None,結果沒有什麼變化,這是因為flask返回值是有限制
3.Flask 中的 Response
訪問如下服務
可以看到返回的是字串
響應頭中的內容型別是文字。這個內容型別決定了瀏覽器使用的渲染方式。
而這裡表示這是個文字內容,要用html的格式顯示出來。這些標籤不是我們返回的,我們只是返回下面顯示的字串。這是瀏覽器讀取到了內容型別,幫我們將字串加了這些標籤。
1.
HTTPResponse("hello") "" str
2.render_template
render 響應模板 render_template("html") str
Django中是template。這裡因為建立多個模板檔案,我們在程式當前目錄建立一個templates目錄。
並且匯入render_template,讓函式返回render_template("模板檔名字"),這樣當客戶端訪問這個路由的時候,就能訪問到這個模板渲染出來的資訊了。
from flask import Flask,render_template
app=Flask(__name__)
@app.route("/index")
def home():
return render_template("index.html")
app.run()
名字寫錯了報錯:
前端檔案如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
我是mcw
</body>
</html>
訪問情況如下,響應頭內容型別中顯示內容型別是文字:
我們可以看到模板檔案深黃色的,這是編輯器層面也就是pycharm給我們新增的可能出現的錯誤的提醒。這都是編輯器查詢不到所出現的錯誤。這是pycharm並不知道我們的模板目錄,我們可以去掉這個顏色。我們可以將這個目錄標記為模板目錄
是否選擇一個支援的語言,
這裡有Django的template語言。我們要選的是Jinja2語言
然後可以看到,模板語言的深黃色標記已經消失
3.redirect("/")
redirect("/") str 以上是Web框架的 Response 三劍客
from flask import Flask,render_template,redirect
app=Flask(__name__)
@app.route("/index")
def home():
return render_template("index.html")
@app.route("/login")
def login():
return redirect("/index")
app.run()
我訪問login的時候,跳轉到index了
檢視login詳情,可以看到響應頭中新增了location,重定向的地址。響應頭中包含location,就會跳轉。而redirect所做的事情就是在響應頭中新增了location
4.send_file()
from flask import Flask,render_template,redirect,send_file app=Flask(__name__) @app.route("/index") def home(): return render_template("index.html") @app.route("/login") def login(): return redirect("/index") @app.route("/get_file") def get_file(): return send_file("git-cmd.exe") app.run()
instance 返回檔案內容,自動識別檔案型別,Content-type中新增檔案型別,Content-type:檔案型別 ** 瀏覽器特性 可識別的Content-type 自動渲染 不可識別的Content-type 會自動下載
我們匯入send_file,
from flask import Flask,render_template,redirect,send_file
app=Flask(__name__)
@app.route("/index")
def home():
return render_template("index.html")
@app.route("/login")
def login():
return redirect("/index")
@app.route("/get_file")
def get_file():
return send_file("app.py")
app.run()
訪問這個url,可以看到,把這個檔案保留原格式的顯示在瀏覽器上了
我們可以看到響應頭的型別中是文字格式,
它給我們新增了html標籤顯示。text/plain就是要保留當前檔案格式的
那麼能不能返回二進位制檔案呢?這裡有張照片
我們返回這個照片
然後訪問,可以看到可以訪問到這個照片了。這是flask給響應頭設定了內容格式。返回檔案內容,自動識別檔案型別,Content-type中新增檔案型別
當放的是個mp3的時侯
瀏覽器上訪問,可以發現黑底,圖示,然後播放。可以點選暫停。這是流媒體,它不會一次就載入完,分很多次給你載入
詳情裡可以看它的內容型別和內容長度。電影也是這樣,分很多次載入。audio是音訊檔案,mp格式的,mpeg就是保護mp3,4等一系列mp開頭的。
檢視渲染的標籤是啥,這裡是video,實際上我們的是audio,那麼為什麼用video渲染呢
下面我們傳送一個mp4的視訊
然後訪問檢視,這裡顯示的就是video了
檢視渲染的還是video,這是因為無論是視訊還是音訊,無法區分的太多,瀏覽器就拿出錯機率最小的來渲染,用video 視訊音訊都可以渲染
假如我們修改為傳送一個Windows可執行檔案,
那麼瀏覽器不能認識,就會讓你下載。x可執行的 ,ms 微軟,下載
這體現一個瀏覽器特性
** 瀏覽器特性 可識別的Content-type 自動渲染 不可識別的Content-type 會自動下載
5.jsonify() str
# 返回標準格式的JSON字串 先序列化JSON的字典,Content-type中加入 Application/json
** Flask 1.1.1 版本中 可以直接返回字典格式,無需jsonify
寫個函式,匯入jsonify。快捷匯入模組,ALT+enter鍵顯示出匯入啥模組,檢視正確就按enter鍵確認匯入,就能快速匯入模組
然後它預設在結尾加上了
如下寫
訪問情況如下:可以看到響應頭內容型別是json,並且有資料長度
如果我們返回的是個字典
那麼它會將字典返回,並且在瀏覽器上顯示出來。如果沒有序列化正常來說是不能返回,但是flask 1.1.1是可以返回字典的,返回的也是application/json格式,實際上它也是使用的jsonify實現的。
如下可以看出來可以直接return的,其中包含字典,實際上它還是會轉成字串,這是1.1.1版本
jsonify() str # 返回標準格式的JSON字串 先序列化JSON的字典,Content-type中加入 Application/json
** Flask 1.1.1 版本中 可以直接返回字典格式,無需jsonify
一般寫api的時候要用到
4.Flask 中的 請求 Request
from flask import Flask,request app=Flask(__name__) @app.route("/login") def login(): #優先判斷 請求方式 #如果是GET請求,返回登入頁 #如果是POST請求 獲取使用者密碼 校驗 if __name__ == '__main__': app.run()
寫一個登陸函式寫上匯入時不執行run,flask中request是一個公共變數,匯入這個變數。
我們可以找到這個變數,這是個公共變數,小寫,常量一般是大寫。公共變數也就是request是可以被多個執行緒修改的。request中有個管理機制叫請求上下文管理
request.method 獲取請求方式
現在函式如下:如果請求方式是get,那麼返回登入也面
from flask import Flask,request,render_template
app=Flask(__name__)
@app.route("/login")
def login():
#優先判斷 請求方式
if request.method == "GET":
#如果是GET請求,返回登入頁
return render_template("login.html")
#如果是POST請求 獲取使用者密碼 校驗
if __name__ == '__main__':
app.run()
登入頁面如下:新增了登入的form表單
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降風雲變</title> </head> <body> <form action="" method="post"> <input type="text" name="username"> <input type="submit" value="登入"> </form> </body> </html>
訪問情況如下:
我輸入內容點選登入如下,顯示方式不被允許。405錯誤碼就是請求方式不被允許,也就是當前的檢視函式不允許post提交
後臺報錯:127.0.0.1 - - [03/Feb/2022 11:12:14] "POST /login HTTP/1.1" 405 -
我們在路由上面新增引數methods,寫上支援的請求方式包括get和post,這裡寫上之後,不是追加,而是覆蓋 ,所以要新增上get,這裡是可迭代物件列表或者元組,然後列印一下form
然後我們再訪問提交
成功列印出來前端提交過來的資料了
提交過來資料是個字典,我們可以讓它直接轉化成字典,可以如下to_dict()操作
也可以用如下方式直接取字典中的值
如果用中括號取索引,當索引未取到時會報錯,所以儘量用get取索引
產生 HTTP 400 錯誤的原因有:
- 1、前端提交資料的欄位名稱或者是欄位型別和後臺的實體類不一致,導致無法封裝;
- 2、前端提交的到後臺的資料應該是 json 字串型別,而前端沒有將物件轉化為字串型別;
@app.route("/login",methods=["GET","POST"]) def login(): #優先判斷 請求方式 #如果是GET請求,返回登入頁 if request.method == "GET": return render_template("login.html") #如果是POST請求 獲取使用者密碼 校驗 else: print("to_dict",request.form.to_dict()) if request.form.get("username") == "mcw": return "mcw login suceess" return "200 OK"
上面就可以對獲取到的使用者做校驗,校驗通過就讓它成功登陸
request中還有別的請求方式。在傳遞url的時候,我們可以使用url進行傳參,那麼我們如果獲取url中的傳參呢,args。如下,我們在url中拼接id=1的引數進行訪問
後端程式獲取url傳參,這個傳參也可以使用轉化到字典的方法。url中除了可以傳遞引數,還可以傳遞檔案,使用的是base64。
** request.values 獲取 url 和 FormData 中的資料 這個裡面有個坑,儘量不用,就是url中的引數會覆蓋掉FormData中的資料
當我們訪問如下地址的時侯
可以看request中的列印資訊:
request.path 請求路徑 路由地址 /login
request.url 訪問請求的完整路徑包括 url引數
request.host 主機位 127.0.0.1:5000 ip+埠
request.cookies 字典獲取瀏覽器請求時帶上的Cookie
下面看request.files,前端新增資料型別,新增file型別的標籤,標籤名稱為my_file
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降風雲變</title> </head> <body> <form action="" method="post"> <input type="text" name="username" formenctype="multipart/form-data"> <input type="file" name="my_file"> <input type="submit" value="登入"> </form> </body> </html>
程式列印一下,files
訪問情況如下:
我們上傳個檔案,點選登入
顯示沒有登入成功
列印出來是這樣的
剛剛位置寫錯了,重新來:
修改正確之後,就能獲取到上傳檔案的資訊了,有一個檔案儲存物件,是我們上傳的圖片
下面我們修改重啟上傳點選登入,可以看到獲取到上傳的檔案儲存物件了
下面我們操作這個檔案儲存物件,將這個檔案儲存到服務端
上傳
檢視結果,可以看到已經使用上傳檔案的檔名為名稱,將檔案儲存到服務端了了。
一般上傳上來我們給它重新命令,比如上傳上來一個頭像,我們這裡建立一個頭像目錄叫avatar。那麼我們首先要找到這個目錄,然後我們將檔名和目錄拼接在一起,最後是save
這樣就成功上傳了檔案
不只是響應當中有content type ,請求當中也有內容型別。如下,enctype
request.json 請求中 Content-Type:application/json 請求體中的資料 被序列化到 request.json 中 以字典的形式存放
request.data 請求中 Content-Type 中不包含 Form 或 FormData 保留請求體中的原始資料 b"" 即Byte。請求的本質上是一個socket傳輸,傳輸不能直接傳字串,得用bytes,這裡就保留流的原始資訊,data一般用的少,比如前端寫錯了,比如型別不識別就儲存原始資料到data裡面
總結:
request.form 獲取FormData中的資料 也就是所謂的Form標籤 to_dict()
request.args 獲取URL中的資料 to_dict()
request.json 請求中 Content-Type:application/json 請求體中的資料 被序列化到 request.json 中 以字典的形式存放
request.data 請求中 Content-Type 中不包含 Form 或 FormData 保留請求體中的原始資料 b""
request.files 獲取Form中的檔案
request.path 請求路徑 路由地址
request.url 訪問請求的完整路徑包括 url引數
request.host 主機位 127.0.0.1:5000
request.cookies 字典獲取瀏覽器請求時帶上的Cookie
** request.values 獲取 url 和 FormData 中的資料 敏感地帶
5.Jinja2
---- template語言
{{ }} 引用 或 執行
{% %} 邏輯引用
1、如下,三個資料結構的資料。初始的程式。
2、這裡app.config["DEBUG"]=True和app.debug=True都是為了實現修改程式之後,無需重啟程式,自動生效。當客戶端訪問過來就訪問的是最新的程式
3、app.run("0.0.0.0",9527) 指定任意地址都能訪問,監聽埠為9527,即服務埠
單個字典
STUDENT = {'name': 'mcw01', 'age': 38, 'gender': '中'},
STUDENT_LIST = [
{'name': 'mcw01', 'age': 38, 'gender': '中'},
{'name': 'mcw02', 'age': 73, 'gender': '男'},
{'name': 'mcw03', 'age': 84, 'gender': '女'}
]
STUDENT_DICT = {
1: {'name': 'mcw01', 'age': 38, 'gender': '中'},
2: {'name': 'mcw02', 'age': 73, 'gender': '男'},
3: {'name': 'mcw03', 'age': 84, 'gender': '女'},
}
from flask import Flask
app=Flask(__name__)
app.config["DEBUG"]=True
app.debug=True
if __name__ == '__main__':
app.run("0.0.0.0",9527)
由於起名字問題,用jinja2了,結果執行這個檔案時把原始碼中所有從jinja2匯入的變成從自己新建檔案匯入,導致很多匯入出錯問題。我把新建檔案改了名字為模板學習,所有從jinja2匯入的變成草叢模板學習匯入的了,雖然把檔名改回來了,但是還有問題的
後來把原始碼改回去了,果然是因為本地起名衝突,導致從本地匯入,結果出現問題了
下面還有很多行出問題,修改一行我就繼續讓它出現下一個,再修改,後面就好了
如下,傳遞一個字典進模板,這裡用的是關鍵字傳參
STUDENT = {'name': 'mcw01', 'age': 38, 'gender': '中'}, STUDENT_LIST = [ {'name': 'mcw01', 'age': 38, 'gender': '中'}, {'name': 'mcw02', 'age': 73, 'gender': '男'}, {'name': 'mcw03', 'age': 84, 'gender': '女'} ] STUDENT_DICT = { 1: {'name': 'mcw01', 'age': 38, 'gender': '中'}, 2: {'name': 'mcw02', 'age': 73, 'gender': '男'}, 3: {'name': 'mcw03', 'age': 84, 'gender': '女'}, } from flask import Flask,render_template app=Flask(__name__) # app.config["DEBUG"]=True app.debug=True @app.route("/") def home(): return render_template("stu.html",stu=STUDENT) if __name__ == '__main__': app.run("0.0.0.0",9527)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降風雲變</title> </head> <body> {{ stu }} </body> </html>
然後在模板裡引用
當瀏覽器訪問的時候,顯示的是字典被包在元組當中,為啥有個元組呢,跟預期的不符合,是因為傳遞的字典多了個逗號,將逗號去掉就好了,前端展示的是字典才是正常的
修改如下:取值可以點取,中括號取,或者get取stu中被傳遞過來的字典中的資料
以上就是單個字典的使用
列表中每個元素都是字典
下面就是列表中多個字典的使用,需要使用for迴圈
使用for迴圈
將列表以關鍵字引數傳遞進去
我不想要中,當不是男不是女的時候想要它是女
這樣就要新增判斷語句,當這個值不是男並且不是女的時候,顯示女,否則顯示當前值
這樣就正常顯示性別了
字典中每個鍵值對的值都是字典1
將它傳進去
迴圈每個字典的鍵,字典加鍵獲取內層字典,然後使用內層字典的資料
結果:
字典中每個鍵值對的值都是字典2
這次不是迴圈字典的鍵,而是迴圈字典的items,items後面記得加括號。也可只拿鍵或者值,keys(),values()
結果:
傳遞自定義函式到模板中,以及將自定義函式作為所有模板共用的函式
自定義一個函式,定義一個檢視函式,在檢視函式中將函式傳遞到模板檔案中
在模板中使用這個函式,
那麼函式的執行結果會渲染在這個模板中,訪問結果如下
下面將這個ab函式設定為模板共用的
只需將函式前面加個裝飾器,就使它變成全域性的模板了,並且不需要傳遞ab這個函式,在模板中就能呼叫這個函式。否則,如果有100個檢視函式,那麼100個檢視函式都要傳遞一次ab函式
訪問結果:
巨集指令(幾乎不用)
前端模板檔案中生成巨集指令,然後在模板中傳參使用,如下栗子:
模板檔案中生成巨集指令,後面再傳參呼叫,就生成想要的標籤,這個直接傳遞函式應該也可以,更好吧。
訪問可以看到生成的標籤
後端生成html傳遞到模板檔案中
前端呼叫一下
可以看到它是字串,這時需要將它轉換成標籤顯示
前端使用safe就可以實現,如果在不能改動前端的基礎上實現將傳遞的字串過去,作為標籤顯示出來,那麼...
不修改前端新增safe的話,那麼給傳遞的標籤字串改為markup字串,即將字串用Markup包起來
後端生成前端html元件的函式
生成的元件,我們可以變成全域性,然後每個模板中呼叫函式,或者是檢視函式中將這個函式傳遞進模板使用
STUDENT = {'name': 'mcw01', 'age': 38, 'gender': '中'} STUDENT_LIST = [ {'name': 'mcw01', 'age': 38, 'gender': '中'}, {'name': 'mcw02', 'age': 73, 'gender': '男'}, {'name': 'mcw03', 'age': 84, 'gender': '女'} ] STUDENT_DICT = { 1: {'name': 'mcw01', 'age': 38, 'gender': '中'}, 2: {'name': 'mcw02', 'age': 73, 'gender': '男'}, 3: {'name': 'mcw03', 'age': 84, 'gender': '女'}, } from flask import Flask,render_template,Markup,session,sessions app=Flask(__name__) # app.config["DEBUG"]=True app.debug=True @app.route("/") def home(): print(STUDENT) return render_template("stu.html",stu=STUDENT,stu_l=STUDENT_LIST,stu_d=STUDENT_DICT) @app.template_global() def ab(a,b): return a+b @app.template_global() def my_input(na,ty): s=f"<input type='{ty}' value='{na}'>" return Markup(s) @app.route("/a") def homea(): inp=Markup("<input type='submit' value='後端給返回的按鈕'>") return render_template("my_a.html",btn=inp) if __name__ == '__main__': app.run("0.0.0.0",9527)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降風雲變</title> </head> <body> {{ ab(4,2) }} {{ stu }}<br> 列表中每個值都是字典: <table BORDER="1" cellspacing="0"> <tr> <td>name</td> <td>age</td> <td>gender</td> </tr> {% for student in stu_l %} <tr> <td>{{ student.name }}</td> <td>{{ student["age"] }}</td> <td> {% if student.get("gender") !="男" and student.get("gender") !="女" %} 女 {% else %} {{ student.get("gender") }} {% endif %} </td> </tr> {% endfor %} </table> 字典中的每個值都是字典: <table BORDER="1" cellspacing="0"> <tr> <td>鍵</td> <td>name</td> <td>age</td> <td>gender</td> </tr> {# {% for skey in stu_d %}#} {# <tr>#} {# <td>{{ stu_d.get(skey).name }}</td>#} {# <td>{{ stu_d.get(skey)["age"] }}</td>#} {# <td>#} {# {% if stu_d.get(skey).get("gender") !="男" and stu_d.get(skey).get("gender") !="女" %}#} {# 女#} {# {% else %}#} {# {{ stu_d.get(skey).get("gender") }}#} {# {% endif %}#} {# </td>#} {# </tr>#} {# {% endfor %}#} {% for skey,svlue in stu_d.items() %} <tr> <td>{{ skey }}</td> <td>{{ svlue.name }}</td> <td>{{ svlue["age"] }}</td> <td> {% if svlue.get("gender") !="男" and svlue.get("gender") !="女" %} 女 {% else %} {{ svlue.get("gender") }} {% endif %} </td> </tr> {% endfor %} </table> </body> </html>
Flask中的session
簡單介紹
session和request在這裡,除了session和request不一樣,其它都是一樣的。
這裡的session基於請求上下文機制
當匯入session的時候,可以看到有個sessions,這是個檔案,session都是基於這個檔案來的
使用簡單栗子
先寫一個登入函式
@app.route("/login",methods=["GET","POST"]) def login(): if request.method == "GET": return render_template("login.html") else: user_info=request.form.to_dict() print(user_info) return "login OK!"
可以正常訪問
現在我們需要的不是列印userinfo,而是獲取到使用者名稱然後存進session中
獲取到使用者名稱並儲存進session中,但是報錯了
RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret.
為什麼後端也能看到報錯資訊呢,在這裡是因為debug現在設定的true,所以會把後端報錯對映到前端展示出來
翻譯一下。我們的flask應用承載在workzauk上,workzauk是個wsgi,wsgi收到請求之後把請求序列化了,然後扔給我們的應用,我們的應用就是flask例項。也就是我們的app就是我們的application。
那麼我們就在應用上生成一個secret_key。這裡隨便寫的
app.secret_key="$@IIO#JD)@(&@^#$NFNAJOOK;k%@#)FJO(@*$&#*ccsia86"
我們再次請求試試
在session中獲取到值,也就是我們成功的把前端提交的資料儲存到session中了
session儲存機制以及使用
Django的session是存到資料庫裡了,那麼flask的session是存到哪裡了呢。session的生命週期,這裡預設是31天。如果把session中獲取的資料放到get方式下,當get請求時,也是每次請求都會列印出這個session的內容。即使把pycharm關掉重新開啟,然後訪問還是可以列印出來。
我們可以看到session儲存到瀏覽器上面了。在應用的cookies下可以看到
當我們把它刪除掉之後,
傳送一個get請求,可以發現獲取的值是None了,之前獲取的是mcw,正常的資料。當我們重新輸入名字,點選登入,就會重新產生session又能重新獲取到了。
現在瀏覽器上重新登入後,有了新的session了
我在secret_key開頭加個1。
然後再次訪問,可以看到獲取到的是None
當把新增的1去掉後,重新訪問
發現又有了。這是因為金鑰被修改了,原來加密的用被修改過的金鑰配不上,
session登入狀態,單個函式使用
如下,現在/a的頁面如下:
/login頁面如下:
我需要實現當訪問/a頁面時,如果登入了,就正常訪問;如果沒有登入,那麼返回登入頁進行登入
STUDENT = {'name': 'mcw01', 'age': 38, 'gender': '中'} STUDENT_LIST = [ {'name': 'mcw01', 'age': 38, 'gender': '中'}, {'name': 'mcw02', 'age': 73, 'gender': '男'}, {'name': 'mcw03', 'age': 84, 'gender': '女'} ] STUDENT_DICT = { 1: {'name': 'mcw01', 'age': 38, 'gender': '中'}, 2: {'name': 'mcw02', 'age': 73, 'gender': '男'}, 3: {'name': 'mcw03', 'age': 84, 'gender': '女'}, } from flask import Flask, render_template, Markup, session, sessions, request, redirect app=Flask(__name__) # app.config["DEBUG"]=True app.debug=True app.secret_key="$@IIO#JD)@(&@^#$NFNAJOOK;k%@#)FJO(@*$&#*ccsia86" @app.route("/") def home(): print(STUDENT) return render_template("stu.html",stu=STUDENT,stu_l=STUDENT_LIST,stu_d=STUDENT_DICT) @app.template_global() def ab(a,b): return a+b @app.template_global() def my_input(na,ty): s=f"<input type='{ty}' value='{na}'>" return Markup(s) @app.route("/a") def homea(): #校驗使用者登入狀態? #校驗session中有沒有user Key if session.get("user"): inp=Markup("<input type='submit' value='後端給返回的按鈕'>") return render_template("my_a.html",btn=inp) #校驗失敗,跳轉login else: return redirect("/login") @app.route("/login",methods=["GET","POST"]) def login(): if request.method == "GET": print(session.get("user")) return render_template("login.html") else: user_info=request.form.to_dict() session["user"]=user_info.get("username") print(session.get("user")) return "login OK!" if __name__ == '__main__': app.run("0.0.0.0",9527)
現在能正常訪問,我把session刪掉重新訪問
刪除session之後,訪問/a就302跳轉到/login了,符合預期
session多個頁面使用,多層裝飾器使用
雖然/a實現了登入才能訪問,但是/首頁卻是沒有實現,如果每個檢視函式都寫一遍,顯然是不可能。
既然不能每個檢視函式都寫,那麼就用裝飾器實現,這裡先寫一個裝飾器
多層裝飾器的執行順序是自下而上執行的。我們需要看這個最終裝飾的函式是誰,再去做裝飾器的疊加。war裝飾之後返回的是inner,inner就是home,home需要被route裝飾。route在war上面,那麼war返回的home就被route裝飾,如果沒有route裝飾,那麼肯定就無法響應資料。比如war放到route上了,那麼route先給響應了,還沒有到war裝飾呢。所以route放到最外層,它要做響應資料。而這裡的war我們是為了做session校驗用的。
如下,就完成了使用裝飾器進行session校驗。在每個檢視函式前加上這個裝飾器
現在我們再次訪問/,發現302跳轉到登入頁了
當我們登入有了session之後
再次訪問/,直接訪問成功
程式彙總
STUDENT = {'name': 'mcw01', 'age': 38, 'gender': '中'} STUDENT_LIST = [ {'name': 'mcw01', 'age': 38, 'gender': '中'}, {'name': 'mcw02', 'age': 73, 'gender': '男'}, {'name': 'mcw03', 'age': 84, 'gender': '女'} ] STUDENT_DICT = { 1: {'name': 'mcw01', 'age': 38, 'gender': '中'}, 2: {'name': 'mcw02', 'age': 73, 'gender': '男'}, 3: {'name': 'mcw03', 'age': 84, 'gender': '女'}, } from flask import Flask, render_template, Markup, session, sessions, request, redirect app=Flask(__name__) # app.config["DEBUG"]=True app.debug=True app.secret_key="$@IIO#JD)@(&@^#$NFNAJOOK;k%@#)FJO(@*$&#*ccsia86" def war(func): def inner(*args,**kwargs): #校驗session if session.get("user"): ret=func(*args,**kwargs) #func=home return ret else: return redirect("/login") return inner @app.route("/") @war def home(): print(STUDENT) return render_template("stu.html",stu=STUDENT,stu_l=STUDENT_LIST,stu_d=STUDENT_DICT) @app.template_global() def ab(a,b): return a+b @app.template_global() def my_input(na,ty): s=f"<input type='{ty}' value='{na}'>" return Markup(s) @app.route("/a") def homea(): #校驗使用者登入狀態? #校驗session中有沒有user Key if session.get("user"): inp=Markup("<input type='submit' value='後端給返回的按鈕'>") return render_template("my_a.html",btn=inp) #校驗失敗,跳轉login else: return redirect("/login") @app.route("/login",methods=["GET","POST"]) def login(): if request.method == "GET": print(session.get("user")) return render_template("login.html") else: user_info=request.form.to_dict() session["user"]=user_info.get("username") print(session.get("user")) return "login OK!" if __name__ == '__main__': app.run("0.0.0.0",9527)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 我是mcw </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降風雲變</title> </head> <body> <form action="" method="post" enctype="multipart/form-data"> <input type="text" name="username" > <input type="file" name="my_file"> <input type="submit" value="登入"> </form> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ ab(1,2) }} {% macro my_input(na,ty) %} <input type="{{ ty }}" name="{{ na }}"> {% endmacro %} {{ my_input("username","password") }} {{ btn }} </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降風雲變</title> </head> <body> {{ ab(4,2) }} {{ stu }}<br> 列表中每個值都是字典: <table BORDER="1" cellspacing="0"> <tr> <td>name</td> <td>age</td> <td>gender</td> </tr> {% for student in stu_l %} <tr> <td>{{ student.name }}</td> <td>{{ student["age"] }}</td> <td> {% if student.get("gender") !="男" and student.get("gender") !="女" %} 女 {% else %} {{ student.get("gender") }} {% endif %} </td> </tr> {% endfor %} </table> 字典中的每個值都是字典: <table BORDER="1" cellspacing="0"> <tr> <td>鍵</td> <td>name</td> <td>age</td> <td>gender</td> </tr> {# {% for skey in stu_d %}#} {# <tr>#} {# <td>{{ stu_d.get(skey).name }}</td>#} {# <td>{{ stu_d.get(skey)["age"] }}</td>#} {# <td>#} {# {% if stu_d.get(skey).get("gender") !="男" and stu_d.get(skey).get("gender") !="女" %}#} {# 女#} {# {% else %}#} {# {{ stu_d.get(skey).get("gender") }}#} {# {% endif %}#} {# </td>#} {# </tr>#} {# {% endfor %}#} {% for skey,svlue in stu_d.items() %} <tr> <td>{{ skey }}</td> <td>{{ svlue.name }}</td> <td>{{ svlue["age"] }}</td> <td> {% if svlue.get("gender") !="男" and svlue.get("gender") !="女" %} 女 {% else %} {{ svlue.get("gender") }} {% endif %} </td> </tr> {% endfor %} </table> </body> </html>
import os from flask import Flask,request,render_template app=Flask(__name__) @app.route("/login",methods=["GET","POST"]) def login(): #優先判斷 請求方式 #如果是GET請求,返回登入頁 if request.method == "GET": return render_template("login.html") #如果是POST請求 獲取使用者密碼 校驗 else: my_file=request.files.get("my_file") filename=my_file.filename #獲取原始檔名 file_path=os.path.join("avatar",filename) my_file.save(file_path) if request.form.get("username") == "mcw": return "mcw login suceess" return "login not OK" if __name__ == '__main__': app.run()