flask學習1

 

總結

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語言
{{ }} 引用 或 執行
{% %} 邏輯引用

View Code

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

View Code

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

View Code

寫一個登陸函數寫上導入時不執行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"

View Code

 

 

 上面就可以對獲取到的用戶做校驗,校驗通過就讓它成功登陸

 

 

 

 

 

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>

View Code

 

 

 程序打印一下,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)

View Code

 

 現在能正常訪問,我把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>

index.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>

login.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>

my_a.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>

stu.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()

app.py

 

Tags: