前言:通過一個簡單的用戶認證頁面來認識flask:涉及到的點如下:
1、app = Flask(__name__) # app是一個flask類的對象,其中__name__表示當前模塊的名字。
2、app.secret_key = "asdasdqw" # 給session加鹽(必須要寫,否則報錯,session需要加密)。
3、app=Flask(__name__,template_folder=) # 其中template_folder裏面定義了默認目錄爲"templates",也可以自定義目錄名。
4、@app.route("/login",methods=["GET","POST"]) # 不寫methods默認只支持GET方法,其他PUT,DELETE都可以繼續加。
5、request.form 獲取POST傳過來的值,request.args 獲取GET傳過來的值。
6、render_template("datail.html", info=info) # 把info傳遞給前端,前端接收爲{{info}}。
7、user = request.form.get("user") # 創建一個session個一個用戶。
8、user_info = session.get("user_info") #獲取session的值。
一、Python 代碼部分:
from flask import Flask,render_template,request,redirect,session
app = Flask(__name__) # app是一個flask類的對象,
# app=Flask(__name__,template_folder=) # 其中template_folder裏面定義了默認目錄爲"templates"
app.secret_key = "asdasdqw" # 給session加鹽
app.debug = True
user_dict = {
"1":{"name":"szq","age":18},
"2":{"name":"sudada","age":28},
"3":{"name":"wsp","age":38},
}
# 查看用戶詳細信息的頁面
@app.route("/datail",methods=["GET","POST"])
def datail():
uid = request.args.get("uid")
info = user_dict.get(uid) # 通過字典的key獲取對應的value
return render_template("datail.html", info=info)
# 登錄頁面
@app.route("/login",methods=["GET","POST"]) # 不寫methods默認只支持GET方法,其他PUT,DELETE都可以繼續加
def login():
user_info = session.get("user_info") # 通過session的值來判斷是否登錄成功
if user_info:
return redirect("/index")
if request.method == "GET":
return render_template("login.html")
user = request.form.get("user")
pwd = request.form.get("pwd")
# request.form 獲取POST傳過來的值
# request.args 獲取GET傳過來的值
if user == "szq" and pwd == "123":
session["user_info"] = user # 用戶登錄成功之後把用戶信息放入session裏面(默認放在cookie裏面),session可以看成是一個字典組成的
return redirect("/index")
return render_template("login.html", msg = "用戶名或者密碼錯誤") # 通過msg=""把值傳遞給html頁面內的{{msg}}
# 登出頁面
@app.route("/logout",methods=["GET","POST"])
def logout():
del session["user_info"]
return redirect("/login")
# 登錄成功頁面
@app.route("/index",methods=["GET"])
def index():
user_info = session.get("user_info") # 通過session的值來判斷是否登錄成功
if not user_info:
return redirect("/login")
return render_template("index.html",user_dict=user_dict)
if __name__ == '__main__':
app.run()
二、HTML 頁面部分:
2.1、login.html如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用戶登錄</title>
</head>
<body>
<h1>login</h1>
<form method="post">
<input type="text" name="user">
<input type="password" name="pwd">
<input type="submit" value="提交"> {{msg}}
</form>
</body>
</html>
2.2、index.html如下:
<body>
<ul>
<!-- user_dict.keys()拿到的是字典的key,user_dict.values()拿到的是字典的value, user_dict.items()拿到的是字典 -->
{% for k,v in user_dict.items() %}
<li>{{ v.name }} <a href="/datail?uid={{k}}">查看詳細</a></li>
{% endfor %}
</ul>
</body>
2.3、datail.html如下:
<body>
<h1>詳細信息</h1>
<div>{{info.name}}</div>
<div>{{info.age}}</div>
</body>
9、flask框架下,各種環境下配置文件的自定義,主要使用:app.config.from_object("settings.Testing") 來實現。
# 一、主體文件內容:
from flask import Flask
app=Flask(__name__)
app.config.from_object("settings.Testing")
# 從settings.py文件裏面找到Testing類[將類裏面所有的靜態字段(大寫)拿到並放入到(當前文件)配置裏面去]
@app.route("/index",methods=["GET","POST"])
def index():
return "index"
if __name__ == '__main__':
app.run()
# 二、settings.py 文件配置如下:
class BaseConfig():
DEBUG = True
SECRET_KEY = "jkoiwmnsiuhvlkw"
class ProductionConfig(BaseConfig):
DATABASE_URI="mysql://user@prod/foo"
DEBUG = False
class DevelopmentConfig(BaseConfig):
DATABASE_URI = "mysql://user@dev/foo"
pass
class Testing(BaseConfig):
DATABASE_URI = "mysql://user@test/foo"
pass
一、Flask框架之創建路由的2種方式
1.1、通過 route("/index") 的方式 #直接寫路由名稱(一般都是有這種)
from flask import Flask, render_template, redirect, request
app=Flask(__name__)
@app.route("/index",methods=["GET","POST"])
def index():
return "index"
if __name__ == "__main__":
app.run()
1.2、通過 app.add_url_rule("/order",view_func=order) 的方式 #寫路由名稱加指定對應的響應函數
from flask import Flask, render_template, redirect, request, url_for
app=Flask(__name__)
def order():
return "order"
app.add_url_rule("/order",view_func=order) # view_func後面傳入函數名
if __name__ == "__main__":
app.run()
二、Flask框架之反向生成URL(獲取請求URL的值,並給這個值起一個別名,這個別名可以直接被redirect使用)
2.1、兩種方式
1.通過 endpoint 的方式定義一個別名(目的是爲了通過這個別名獲取請求URL的值);
2.使用 url_for 默認傳入函數名的方式獲取請求URL的值(不需要endpoint定義別名了)。
from flask import Flask, redirect, url_for
app=Flask(__name__)
# endpoint="n1" 獲取請求的URL的值(或者說是給URL起一個別名)
@app.route("/index",methods=["GET","POST"],endpoint="n1")
def index():
v1 = url_for("n1") # 1.通過 endpoint 的方式定義一個別名"n1",n1拿到值就是"/index"
v2 = url_for("login") # 2.使用 url_for 默認傳入函數名的方式獲取請求URL的值"/login"(不需要endpoint定義別名了)。
print(v1,v2) # /index /login
return redirect(v2) # 別名可以直接當做URL來使用
@app.route("/login",methods=["GET","POST"])
def login():
return "login"
if __name__ == "__main__":
app.run()
三、Flask框架之自定義路由轉換器
3.1、路由系統:
<int:nid>意思:index函數將被傳遞一個參數(int類型),傳遞的值爲"nid"。
<username>意思:index函數將被傳遞一個參數(str類型),傳遞的值爲"username"。
<float>意思:index函數將被傳遞一個參數(float類型),傳遞的值爲"float"。
<path:path>意思:index函數將被傳遞一個參數(path類型),傳遞的值爲path(假如訪問路徑爲:/index/abc/def/ang,那麼path的值爲:abc/def/ang)。
from flask import Flask, render_template, redirect, request, url_for
app=Flask(__name__)
# 路由系統:例子如下
# <int:nid>意思:index函數將被傳遞一個參數(int類型),傳遞的值爲"nid"。
# <username>意思:index函數將被傳遞一個參數(str類型),傳遞的值爲"username"。
# <float>意思:index函數將被傳遞一個參數(float類型),傳遞的值爲"float"。
# <path:path>意思:index函數將被傳遞一個參數(path類型),傳遞的值爲path(假如訪問路徑爲:/index/abc/def/ang,那麼path的值爲:abc/def/ang)。
@app.route("/index/<int:nid>",methods=["GET","POST"])
def index(nid):
print(nid) # 請求URL:http://127.0.0.1:5000/index/111,獲取到的值爲 "111"
return "index"
@app.route("/index/<username>",methods=["GET","POST"])
def index(username):
print(username) # 請求URL:http://127.0.0.1:5000/index/aaaa,獲取到的值爲 "aaaa"
return "index"
@app.route("/index/<float>",methods=["GET","POST"])
def index(float):
print(float) # 請求URL:http://127.0.0.1:5000/index/1.3,獲取到的值爲 "1.3"
return "index"
@app.route("/index/<path:path>",methods=["GET","POST"])
def index(path):
print(path) # 請求URL:http://127.0.0.1:5000/index/a/b/c,獲取到的值爲 "a/b/c"
return "index"
if __name__ == "__main__":
app.run()
四、Flask框架之 app.route 參數
4.1、strict_slashes=True|False:決定URL結尾可不可以帶 / 號。
from flask import Flask, render_template, redirect, request, url_for
app = Flask(__name__)
app.debug = True
# strict_slashes=False,URL最後的 / 符號可帶可不帶。
@app.route("/index", methods=["GET", "POST"], strict_slashes=False)
def index():
return "index"
# strict_slashes=True,URL最後的 / 符號不能帶。
@app.route("/login", methods=["GET", "POST"], strict_slashes=True)
def login():
return "login"
if __name__ == "__main__":
app.run()
4.2、重定向URL:301跳轉:redirect_to="/new" 302跳轉:return redirect("/order")
方式一:301
from flask import Flask, render_template, redirect, request, url_for
app=Flask(__name__)
app.debug=True
# 2、redirect_to="/new":重定向URL到"/new"
@app.route("/index",methods=["GET","POST"],redirect_to="/new")
def index():
return "index"
@app.route("/new",methods=["GET","POST"])
def new():
return "new"
if __name__ == "__main__":
app.run()
#===============================分割線================================
方式二:302
from flask import Flask, request, redirect, render_template
app=Flask(__name__)
app.debug=True
@app.route("/index",methods=["GET","POST"])
def index():
return redirect("/order")
@app.route("/order",methods=["GET","POST"])
def order():
return "order"
if __name__ == "__main__":
app.run()
五、Flask框架之獲取子域名的路由
5.1、通過 subdomain="<username>" 獲取2級域名。
# 本地hosts解析:
127.0.0.1 localhost
127.0.0.1 sudada.com
127.0.0.1 www.sudada.com
127.0.0.1 sale.sudada.com
127.0.0.1 buy.sudada.com
# 代碼如下:
from flask import Flask, render_template, redirect, request, url_for
app=Flask(__name__)
app.debug=True
app.config['SERVER_NAME'] = 'sudada.com:5000' # 這裏指定主域名地址+端口。
@app.route("/dynamic",subdomain="<username>") # 域名請求"/dynamic"時獲取請求URL的2級域名。
def index(username):
print(username)
return "dynamic"
if __name__ == "__main__":
app.run()
# 訪問www.sudada.com:5000/dynamic,獲取到的值爲"www"
六、Flask框架之視圖中添加裝飾器
6.1、使用 @functools.wraps(func) 方式,將inner函數僞造成被傳值過來的func函數。
from flask import Flask
app=Flask(__name__)
app.debug=True
import functools
# 爲什麼要有@functools.wraps(func)?
# 1.由於endpoint默認傳入的是函數名,而index和order函數已經被wapper裝飾了。
# 此時的index=wapper(index),真正執行的函數爲wapper裝飾器內的inner函數,所以在index函數內endpoint傳入的就是inner函數。
# 2.當index,order都被wapper裝飾了之後,index函數內的endpoint傳入的是inner函數,
# order函數內的endpoint傳入的也是inner函數,同一個inner函數傳入2次就報錯。
# 3.所以要在wapper裝飾器內使用"@functools.wraps(func)",同時在"@wapper"要寫在"@app.route("/dynamic")"的後面
# [email protected](func)的目的就是將inner函數僞造成被傳值過來的func函數。
def wapper(func):
@functools.wraps(func)
def inner(*args,**kwargs):
print(inner.__name__) # 查看裝飾器內inner函數的名字是"inner",還是被傳遞過來的"func"函數。
return func(*args,**kwargs)
return inner
# 如果wapper裝飾器內不加"@functools.wraps(func)"的話,
# 1、請求"/dynamic"實際上訪問的是裝飾器內的inner函數。
# 2、代碼運行報錯,提示裝飾器內的inner已經存在。
@app.route("/dynamic",methods=["GET","POST"])
@wapper
def dynamic():
return "dynamic"
@app.route("/order",methods=["GET","POST"])
@wapper
def order():
return "order"
if __name__ == "__main__":
app.run()
6.2、Flask框架之常見裝飾器之404報錯自定義,@app.errorhandler(404)
訪問當前視圖輸入的內容不存在時,報錯的404爲自定義的輸出。
# 在視圖裏面加一個"@app.errorhandler(404)"
@app.errorhandler(404)
def page_not_find(error):
return "this page does not exist",404 # 自定義的404輸出
七、Flask框架之請求和響應相關用法
import json
from flask import Flask, request, redirect, render_template, jsonify, make_response
app = Flask(__name__)
app.debug = True
@app.route("/index", methods=["GET", "POST"])
def index():
# 請求相關的常見用法
# request.method # 請求方式
# request.args # 獲取GET請求的數據
# request.form # 獲取POST請求的數據
# request.cookies
# request.headers
# request.path
# 上傳文件使用
# request.files
# obj = request.files["the_file_name"]
# obj.save("/var/www/uploads/" + secure_filename(obj.filename))
# 響應相關
# return jsonify({"aaa":123}) # 通過"jsonify",返回json格式字符串
# return "index" # 返回字符串
# return render_template("index.html",n1=123) # 返回模板配置文件,並通過n1=123傳值
# return redirect("/order") # 重定向302
# 響應相關之設置cookies或者響應頭,通過make_response包裹之後,response就可以進行設置cookie和設置head
# response = make_response(render_template("index.html"))
# response.delete_cookie("key") # 刪除cookies
# response.set_cookie("key","value") # 設置cookies
# response.headers["X-Something"] = "A value" # 設置響應頭部的格式
# return response
return "index"
if __name__ == "__main__":
app.run()
八、Flask框架之模板引擎
8.1、模板調用函數內的數據(字符串,列表,字典,函數)及模板語法傳入全局函數
Python部分:
from flask import Flask, request, redirect, render_template, jsonify, make_response, Markup
app=Flask(__name__)
app.debug=True
# 模板語法之傳入全局函數(每個模板[*.html]都可以調用的函數)
@app.template_global()
def k6(a1,b1):
return a1 + b1
def get_input(value):
return Markup("<input value='%s'>" %value)
# 模板調用
@app.route("/index",methods=["GET","POST"])
def index():
context={
"k1":123,
"k2":[11,22,33],
"k3":{"name":"sudada","age":24},
"k4":lambda x:x+1,
"k5":get_input,
}
return render_template("index.html",**context) # 把字典context作爲參數傳入
if __name__ == "__main__":
app.run()
HTML部分:
<body>
<h1>{{k1}}</h1>
<h1>{{k2[0]}} {{k2.0}}</h1>
<!-- 或者列表內的數據 -->
<h1>{{k3["name"]}} {{k3.name}} {{k3.get("aaa",999)}}</h1>
<!-- 獲取字典內的數據 -->
<h1>{{k4(666)}}</h1>
<!-- k4(666)表示給函數傳遞值666 -->
<h1>{{k5(99)}}</h1>
<!-- "|safe"的功能是將"input"標籤變成真正的框,不加的話會顯示原始字符串,並不會做渲染 -->
<!-- 後端也可以使用"Markup"模塊來實現:return Markup("<input value='%s'>" %value) -->
<h1>{{k6(1,2)}}</h1>
<!-- 執行全局函數k6,(1,2)表示傳入的值 -->
</body>
8.3、模板繼承
父模板:
<body>
<div>頭部</div>
<div>
{% block content %} {% endblock %}
</div>
<div>底部</div>
</body>
子模板繼承:
{% extends "layout.html" %}
{% block content %}
<h1>{{k1}}</h1>
<h1>{{k2[0]}} {{k2.0}}</h1>
<!-- 或者列表內的數據 -->
<h1>{{k3["name"]}} {{k3.name}} {{k3.get("aaa",999)}}</h1>
<!-- 獲取字典內的數據 -->
<h1>{{k4(666)}}</h1>
<!-- k4(666)表示給函數傳遞值666 -->
<h1>{{k5(99)}}</h1>
<!-- "|safe"的功能是將"input"標籤變成真正的框,不加的話會顯示原始字符串,並不會做渲染 -->
<!-- 後端也可以使用"Markup"模塊來實現:return Markup("<input value='%s'>" %value) -->
<h1>{{k6(1,2)}}</h1>
<!-- 執行全局函數k6,(1,2)表示傳入的值 -->
{% endblock %}
九、Flask框架之session使用和源碼流程
from flask import Flask, session
app = Flask(__name__)
app.debug = True
app.secret_key = "asdimwe" # session加鹽
@app.route("/x1", methods=["GET", "POST"])
def index():
# 這個session就是去ctx中獲取一個session
"""
# Session請求過程步驟解析:
# 前言:flask 請求執行的步驟
app.__call__()
app.wsgi_app()
# 1、請求剛到達時:
ctx = self.request_context(environ) == RequestContext(request,session=None)
ctx.push()
# 2、執行視圖函數
# 3、請求結束,通過save_session將設置的session值寫到cookie裏面。
SecureCookieSessionInterface.save_session()
"""
session["k1"] = 123 # 給"k1"這個key賦值123
return "index"
@app.route("/x2", methods=["GET", "POST"])
def order():
print(session["k1"]) # 獲取"k1"對應的值(value)
return "order"
if __name__ == "__main__":
app.run()
十、Flask框架之 before_reqeust 和 after_request
10.1、before_reqeust內不帶有return返回值時
before_reqeust:請求到達視圖函數@app.route("/x1")之前,執行的代碼(先進先出)
after_request:視圖函數@app.route("/x1")執行完畢之後,執行的代碼(先進後出)
from flask import Flask,session
app=Flask(__name__)
app.debug=True
app.secret_key="asdimwe" # session加鹽
# 請求到達視圖函數之前,執行的代碼(先進先出)
@app.before_request
def xxx1():
print("執行前1")
@app.before_request
def xxx2():
print("執行前2")
# 視圖函數執行完畢之後,執行的代碼(先進後出)
@app.after_request
def ooo1(response):
print("執行後1")
return response
@app.after_request
def ooo2(response):
print("執行後2")
return response
#視圖函數
@app.route("/x1",methods=["GET","POST"])
def x1():
print("視圖函數x1")
return "視圖函數x1"
@app.route("/x2",methods=["GET","POST"])
def x2():
print("視圖函數x2")
return "視圖函數x2"
if __name__ == "__main__":
app.run()
# 執行結果:
執行前1
執行前2
視圖函數x1
執行後2
執行後1
10.2、before_reqeust內帶有return返回值時
如果執行前的函數帶有return返回值的話,那麼將return的值作爲結果返回到前端頁面,同時不再執行視圖函數。並且把執行後的函數執行完。
from flask import Flask
app=Flask(__name__)
app.debug=True
# 請求到達視圖函數之前,執行的代碼(先進先出)
@app.before_request
def xxx1():
print("執行前1")
return "執行前1_stop"
@app.before_request
def xxx2():
print("執行前2")
return "執行前2_stop"
# 視圖函數執行完畢之後,執行的代碼(先進後出)
@app.after_request
def ooo1(response):
print("執行後1")
return response
@app.after_request
def ooo2(response):
print("執行後2")
return response
#視圖函數
@app.route("/x1",methods=["GET","POST"])
def x1():
print("視圖函數x1")
return "視圖函數x1"
@app.route("/x2",methods=["GET","POST"])
def x2():
print("視圖函數x2")
return "視圖函數x2"
if __name__ == "__main__":
app.run()
# 執行結果:
執行前1
執行後2
執行後1
10.3、基於 before_reqeust 實現用戶登錄
from flask import Flask,session,request,redirect,render_template
app=Flask(__name__)
app.debug=True
app.secret_key="asdimwe" # session加鹽
# 執行視圖函數之前,執行before_request
# 用戶登錄認證,通過session來判斷用戶是否登錄,如果Session不存在則判斷爲未登錄需要重新登錄。
@app.before_request
def check_login():
# 如果輸入的URL不是以"login"開頭的,全部都獲取一次session判斷用戶是否登錄。
if request.path != "/login":
user=session.get("user_info")
# 如果用戶未登錄,則跳轉到登錄頁面
if not user:
return redirect("/login")
# 登錄的視圖函數
@app.route("/login",methods=["GET","POST"])
def login():
if request.method == "GET":
return render_template("login.html")
user=request.form.get("user")
pwd=request.form.get("pwd")
if user == "szq" and pwd == "123":
session["user_info"]=user
return redirect("/index")
else:
return render_template("login.html", msg="用戶名或密碼錯誤") # msg關聯"login.html"裏面的{{msg}}
@app.route("/index",methods=["GET","POST"])
def index():
return "首頁"
@app.route("/xxx",methods=["GET","POST"])
def xxx():
return "xxx"
# 刪除session
@app.route("/logout", methods=["GET", "POST"])
def logout():
del session["user_info"]
return "logout"
if __name__ == "__main__":
app.run()
十一、Flask框架之閃現
方式一:添加單個數據
from flask import Flask,flash,get_flashed_messages
app=Flask(__name__)
app.debug=True
app.secret_key="asdkbmn"
@app.route("/x1",methods=["GET","POST"])
def x1():
# 添加一條數據到列表裏(做一次GET請求就會添加一條數據到列表裏面,可做多次get請求),GET:http://127.0.0.1:5000/x1
flash("我要上天1")
return "x1"
@app.route("/x2",methods=["GET","POST"])
def x2():
# 獲取這個數據,GET:http://127.0.0.1:5000/x2,數據獲取完畢之後再次獲取的時候,數據就沒了。
data = get_flashed_messages()
print(data) # "我要上天1"
return "x2"
if __name__ == "__main__":
app.run()
方式二:添加多個數據
from flask import Flask,flash,get_flashed_messages
app=Flask(__name__)
app.debug=True
app.secret_key="asdkbmn"
@app.route("/x1",methods=["GET","POST"])
def x1():
# 添加一條數據,定義一個key(做一次GET請求,可做多次get請求),GET:http://127.0.0.1:5000/x1
flash("我要天1",category="x1")
flash("我要天2",category="x2")
return "x1"
@app.route("/x2",methods=["GET","POST"])
def x2():
# 獲取這個數據,指定對應的key,GET:http://127.0.0.1:5000/x2,獲取完畢之後在做一次GET請求,數據就沒了。
data_x1 = get_flashed_messages(category_filter=["x1"])
data_x2 = get_flashed_messages(category_filter=["x2"])
print(data_x1) # ['我要天1']
print(data_x2) # ['我要天2']
return "x2"
if __name__ == "__main__":
app.run()
十二、藍圖
12.1、三大作用
1、目錄結構的劃分 2、URL的劃分 3、基於藍圖內的視圖單獨實現befor_request
12.2、藍圖基本的目錄結構如下:
12.3、代碼示例
一、manage.py文件
from blueview import app
if __name__ == '__main__':
app.run()
二、__init__.py文件
from flask import Flask
app=Flask(__name__)
app.debug=True
from .views import account
from .views import admin
from .views import user
app.register_blueprint(account.ac,url_prefix="/account")
app.register_blueprint(admin.ad,url_prefix="/admin")
app.register_blueprint(user.us,url_prefix="/user")
三、account.py文件
from flask import Blueprint,render_template
ac=Blueprint("ac",__name__)
@ac.route("/login")
def login():
return render_template("login.html")
@ac.route("/logout")
def logout():
return "logout"
四、user.py文件
from flask import Blueprint
us=Blueprint("us",__name__)
# url_prefix="/user": 訪問當前代碼內所有的視圖函數,請求的URL錢必須帶有"/user"。
# 還可以通過static_folder=,和template_folder=單獨制定目錄。
# 目錄的優先級順序爲: 優先去__init__內查找對應的目錄,若目錄不存在則去藍圖內找當前定義的目錄。
@us.route("/info")
def info():
return "info"
五、admin.py文件
from flask import Blueprint
ad=Blueprint("ad",__name__)
@ad.route("/home")
def home():
return "home"
十三、pipreqs
功能:到項目裏邊,自動找到項目裏面依賴了哪些模板,並且找到模塊對應的版本,然後生成一個文件"requirements.txt"。
使用方法:
1、安裝命令:pip3 install pipreqs
2、生成依賴文件(進入到項目目錄下):pipreqs ./
3、安裝依賴文件:pip3 install -r requirements.txt
十四、延伸部分--什麼是函數?什麼是方法?如何區分。
舉例說明:
from types import MethodType,FunctionType
class Foo(object):
def fetch(self):
pass
# 類直接調用函數,它就是函數
print(isinstance(Foo.fetch,MethodType)) # False
print(isinstance(Foo.fetch,FunctionType)) # True
# 實例化類之後,通過對象去調用方法(函數),它就是方法
obj=Foo()
print(isinstance(Foo.fetch,MethodType)) # True
print(isinstance(Foo.fetch,FunctionType)) # False