python 全棧開發,Day120(路由系統, 實例化Flask的參數, 藍圖(BluePrint), before_request after_request)


昨日內容回顧

1.Flask:
    from flask import Flask
    app = Flask(__name__)
    # 從源碼中可以看出,Flask集成的run方法是由werkzeug中的run_simple方法提供的。
    app.run() # run_simple(host,port,obj_or_func())
    obj() 對象加括號相當於調用 __call__() 方法

2.簡單路由
    @app.route("/index",methods=["GET","POST"])
    def index(){
        return
    }
    默認GET請求,如果改寫了methods,GET一定要加上

3.flask 中的返回值:
    左邊是django,右邊是flask

    HttpResponse  == return "OK!"
    render == render_template
    redirect == redirect

    return send_file() # 將一個文件打開,然后返回文件內容


4.request:
    request
    form:POST中的FormData數據存放在form(字典) 通過字典的方式去取值
    args:URL參數存放在args(字典) 通過字典的方式去取值
    values: url + FormData .to_dict()同名key url覆蓋Formdata
    data: 當 content-type:None 也就是無法處理時,將數據存放在data中 並且以b"{k:v}"形式存儲,是bytes類型
    json: 當 content-type:application/json 數據以Json放存放

5.Flask Jinja2
    {{}} 非邏輯
    {%%} 包含邏輯

    {% for i in a %}
    {% endfor %}

    {% if True %}
    {% endif %}

    前端:| safe :安全標簽字符串兒
    后端: Markup(tag) :安全標簽字符串兒

    @app.template_global() # 定義模板全局函數
    def func():
        return 1+1

    前端:
    {{ func() }}

    @app.template_filter() # 定義模板全局函數,類似偏函數
    def func(a):
        return a

    前端:
    {{ 1 | func() }}

    extends 模板繼承
    include 加載模板

    宏定義:很少用
    {% macro func(ty , na) %}
        <input type="{{ ty }}" name="{{ na }}" >
    {% endmacro %}
    調用:
    func("text","username")


6.flask 中的 session
    app.secret_key = "alex DSB"
    session 序列化一個加密字符串,存在前端的cookie。它的鍵名就是session
    當發起請求的時候,將字符串發送到后端反序列化,拿出存放在服務器端的session
    session["key"] = "value" # 就是字典

    注意:cookie存儲在瀏覽器中,session存儲在服務器中。
    cookie相當於鑰匙,session相當於銀行保險櫃!
View Code

昨日作業講解

昨天的作業就是,有3個視圖函數,分別是/login,/student_list,/student_detail。寫一個裝飾器,除了/login以外,其他視圖函數都要登錄才行!

使用session判斷!

原始代碼

from flask import Flask,render_template,sessions,request,redirect

app = Flask(__name__)

USER = {'username': 'xiao', 'password': "123"}


@app.route("/login",methods=["POST","GET"])
def login():
    if request.method == "GET":
        # 前端模板中使用了msg,這里就算是傳遞空,也要出現msg
        return render_template("login.html", msg="")

    if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]:
        return redirect("/student_list")

    return render_template("login.html", msg="用戶名密碼錯誤")


@app.route("/student_list")
def student_list():
    return "學生列表"

@app.route("/student_detail")
def student_detail():
    return "學生詳情"


if __name__ == '__main__':
    app.run("0.0.0.0", 5000, debug=True)
View Code

 

使用裝飾器

from flask import Flask,render_template,session,request,redirect

app = Flask(__name__)
# 使用session,必須設置secret_key
app.secret_key = "123asdzxc"

USER = {'username': 'xiao', 'password': "123"}

def auth(func):
    def inner(*args,**kwargs):
        if session.get("user"):
            return func(*args,**kwargs)
        else:
            return redirect("/login")

    return inner

@app.route("/login",methods=["POST","GET"])
def login():
    if request.method == "GET":
        # 前端模板中使用了msg,這里就算是傳遞空,也要出現msg
        return render_template("login.html", msg="")

    username = request.form["username"]  # 獲取POST請求時,FormData中的參數
    # password = request.form.get("password")
    if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]:
        # 設置session
        session["user"] = username
        return redirect("/student_list")

    return render_template("login.html", msg="用戶名密碼錯誤")


@app.route("/student_list",endpoint="student_list")
@auth
def student_list():
    return "學生列表"

@app.route("/student_detail",endpoint="student_detail")
@auth
def student_detail():
    return "學生詳情"


if __name__ == '__main__':
    app.run("0.0.0.0", 5000, debug=True)
View Code

 

重啟flask,直接訪問student_list

http://127.0.0.1:5000/student_list

打開瀏覽器工具,查看網絡。它會跳轉至登錄頁面!

輸入正確的用戶名和密碼

提交之后,跳轉頁面

 

 注意:使用裝飾器的視圖函數,必須要定義endpoint參數。

因為使用裝飾器之后,視圖函數都是inner,所以flask無法區分,路由到底指向哪一個視圖函數。

啟動flask之后,會直接報錯。endpoint參數,是給url起別名,唯一標識。可以做url反向解析!

 

還有一種方法,使用@functools.wraps裝飾器,保留原函數信息,比如函數名。

但是不推薦使用。因為定義視圖函數,本身就應該定義endpoint參數

 

一、路由系統

Flask中的路由系統其實我們並不陌生了,從一開始到現在都一直在應用

@app.route("/",methods=["GET","POST"])

為什么要這么用?其中的工作原理我們知道多少?

@app.route() 裝飾器中的參數

1. @app.route() 裝飾器中的參數

如果不明白裝飾器 點擊這里

methods

methods : 當前 url 地址,允許訪問的請求方式

from flask import Flask,request

app = Flask(__name__)

@app.route("/info", methods=["GET", "POST"])
def student_info():
    stu_id = int(request.args["id"])
    return f"hello kitty {stu_id}"  # Python3.6的新特性 f"{變量名}"

if __name__ == '__main__':
    app.run("0.0.0.0", 5000, debug=True)
View Code

訪問url,注意:要帶上參數id,否則報錯

http://127.0.0.1:5000/info?id=1

效果如下:

endpoint

endpoint : 反向url地址,默認為視圖函數名 (url_for)

from flask import Flask,request,url_for

app = Flask(__name__)

@app.route("/info", methods=["GET", "POST"],endpoint="r_info")
def student_info():
    print(url_for("r_info"))  # /info
    stu_id = int(request.args["id"])
    return f"hello kitty {stu_id}"  # Python3.6的新特性 f"{變量名}"

if __name__ == '__main__':
    app.run("0.0.0.0", 5000, debug=True)
View Code

刷新頁面,效果同上!查看Pycharm控制台輸出:

/info

注意:這並不是完整的url。如果要給前端妹子,返回一個完整的URL怎么辦呢?

url_for

url_for:用於反向生成url,也可以附帶一些參數,比如想要完整的URL,可以設置_external為Ture:

from flask import Flask,request,url_for

app = Flask(__name__)

@app.route("/info", methods=["GET", "POST"],endpoint="r_info")
def student_info():
    stu_id = int(request.args["id"])
    print(url_for("r_info", _external=True))
    return f"hello kitty {stu_id}"  # Python3.6的新特性 f"{變量名}"

if __name__ == '__main__':
    app.run("0.0.0.0", 5000, debug=True)
View Code

刷新頁面,效果同上!查看Pycharm控制台輸出:

http://127.0.0.1:5000/info

 

但是還不夠,參數沒有啊?怎么辦?再加上url參數。

注意:由於id是動態參數,所以后台獲取時,要和實際的參數匹配。這里是id

from flask import Flask,request,url_for

app = Flask(__name__)

@app.route("/info", methods=["GET", "POST"],endpoint="r_info")
def student_info():
    stu_id = int(request.args["id"])
    print(url_for("r_info", _external=True,id=stu_id))
    return f"hello kitty {stu_id}"  # Python3.6的新特性 f"{變量名}"

if __name__ == '__main__':
    app.run("0.0.0.0", 5000, debug=True)
View Code

刷新頁面,效果同上!查看Pycharm控制台輸出:

http://127.0.0.1:5000/info?id=1

這下,就非常完美了!

defaults

defaults : 視圖函數的參數默認值{"nid":100}

注意:視圖函數必須要設置形參nid,否則報錯!

from flask import Flask,request,url_for

app = Flask(__name__)

@app.route("/info", methods=["GET", "POST"],endpoint="r_info",defaults={"nid": 100})
def student_info(nid):
    # stu_id = int(request.args["nid"])
    print(url_for("r_info", _external=True))
    print(nid)
    return f"hello kitty {nid}"  # Python3.6的新特性 f"{變量名}"

if __name__ == '__main__':
    app.run("0.0.0.0", 5000, debug=True)
View Code

 

訪問url:  http://127.0.0.1:5000/info

效果如下:

strict_slashes

strict_slashes : url地址結尾符"/"的控制 False : 無論結尾 "/" 是否存在均可以訪問 , True : 結尾必須不能是 "/"

from flask import Flask,request,url_for

app = Flask(__name__)

@app.route("/info", strict_slashes=True)
def student_info():
    return "hello kitty info"

if __name__ == '__main__':
    app.run("0.0.0.0", 5000, debug=True)
View Code

 

末尾不帶 "/"

 

 

末尾帶 "/"

也就是說:strict_slashes = True ,表示開啟路由嚴格匹配模式!即使末尾多了一個"/",也不允許訪問!

 

將參數改為False

from flask import Flask,request,url_for

app = Flask(__name__)

@app.route("/info", strict_slashes=False)
def student_info():
    return "hello kitty info"

if __name__ == '__main__':
    app.run("0.0.0.0", 5000, debug=True)
View Code

 

刷新頁面,又可以訪問了

如果多加一個s,會報404

總結:strict_slashes 用來控制末尾是否有"/",為Ture,帶 "/"不允許!

為False,不管你帶不帶"/",都可以訪問!

redirect_to

redirect_to : url地址重定向

from flask import Flask,request,url_for

app = Flask(__name__)

@app.route("/info", redirect_to="/infos")
def student_info():
    return "hello kitty info"

@app.route("/infos")
def student_infos():
    return "Hello Tom infos"

if __name__ == '__main__':
    app.run("0.0.0.0", 5000, debug=True)
View Code

訪問url:  http://127.0.0.1:5000/info,會發生重定向

查看瀏覽器工具,查看網絡。它經歷了2次請求!

它有什么應用場景呢?

比如你的網站域名是xx.com,后來你的網頁改版了,換了新的域名qqxx.com。但是老客戶還不知道你的新域名啊!怎么辦呢?用重定向就可以了!

subdomain

subdomain : 子域名前綴 subdomian="qq" 這樣寫可以得到 qq.xx.com 前提是app.config["SERVER_NAME"] = "xx.com:5000"

from flask import Flask,request,url_for

app = Flask(__name__)

# 一定一定一定要寫端口!!!!!!
app.config["SERVER_NAME"] = "xx.com:5000"

@app.route("/",endpoint="r_info",subdomain="qq")
def student_info():
    print(url_for("r_info", _external=True,))
    return "hello kitty info"

if __name__ == '__main__':
    # 監聽端口為5000,注意:要和SERVER_NAME匹配!
    app.run("0.0.0.0", 5000, debug=True)
View Code

注意:app.config["SERVER_NAME"] = "xx.com:5000"。

一定要加端口!一定要加端口!一定要加端口!重要的事情說三遍!!!

就是因為沒有加端口,快要放棄了!google了一把,終於找到原因了!

 

這里是window 10訪問。必須要修改本機的Hosts文件,至於怎么修改,為啥沒有權限,請自行百度!

修改Hosts文件,添加一條記錄

127.0.0.1    qq.xx.com

打開cmd窗口,ping qq.xx.com,請確保返回地址是127.0.0.1

 

打開瀏覽器訪問url,注意:只能是qq.xx.com訪問。不能是xx.com!訪問時,一定要帶上端口!

http://qq.xx.com:5000/

效果如下:

 

關於路由目前就說這么多,之后的課程中會有關於Flask路由系統的源碼剖析,再詳細說明Flask路由系統的工作原理

 

動態參數路由

2.動態參數路由:

<int:參數名> 參數轉換為整形

<int:nid> 就是在url后定義一個參數接收

但是這種動態參數路由,在url_for的時候,一定要將動態參數名+參數值添加進去,否則會拋出參數錯誤的異常

from flask import Flask,request,url_for

app = Flask(__name__)

@app.route("/info/<int:nid>", endpoint="r_info")
def student_info(nid):
    print(url_for("r_info", _external=True,nid=nid))
    return f"hello kitty {nid}"  # Python3.6的新特性 f"{變量名}"

if __name__ == '__main__':
    # 監聽端口為5000
    app.run("0.0.0.0", 5000, debug=True)
View Code

訪問url,一定要帶上參數,而且參數必須是數字!

查看Pycharm控制台輸出:

12 <class 'int'>
http://127.0.0.1:5000/info/12

 

如果是字符串,會報錯

 

<string:參數名> 參數轉換為字符串

from flask import Flask,request,url_for

app = Flask(__name__)

@app.route("/info/<string:nid>", endpoint="r_info")
def student_info(nid):
    print(nid,type(nid))
    print(url_for("r_info", _external=True,nid=nid))
    return f"hello kitty {nid}"  # Python3.6的新特性 f"{變量名}"

if __name__ == '__main__':
    # 監聽端口為5000
    app.run("0.0.0.0", 5000, debug=True)
View Code

刷新頁面,就可以訪問了

查看Pycharm控制台輸出:

ask <class 'str'>
http://127.0.0.1:5000/info/ask

 

參數是數字也是正常的

路由正則

3.路由正則:

一般不用,如果有特殊需求,不怕麻煩的話,這個東西還是挺好用的,前提是你的正則玩兒的很6

from flask import Flask,request,url_for
# 導入轉換器基類
from werkzeug.routing import BaseConverter

app = Flask(__name__)

# 自定義正則轉換器
class RegexConverter(BaseConverter):
    def __init__(self, url_map, *args):
        super(RegexConverter, self).__init__(url_map)
        # 將接受的第1個參數當作匹配規則進行保存
        self.regex = args[0]


# 將自定義轉換器添加到轉換器字典中,並指定轉換器使用時名字為: re
app.url_map.converters['re'] = RegexConverter

# 使用轉換器去實現自定義匹配規則
# 當前此處定義的規則是:3位數字
@app.route('/info/<re("[0-9]{3}"):nid>', endpoint="r_info")
def student_info(nid):
    print(url_for("r_info", _external=True,nid=nid))
    return f"nid 為 {nid}"  # Python3.6的新特性 f"{變量名}"

if __name__ == '__main__':
    # 監聽端口為5000
    app.run("0.0.0.0", 5000, debug=True)
View Code

訪問url,注意:參數必須是3位數字

 

本文參考:

https://www.cnblogs.com/DragonFire/p/9260295.html

二、實例化Flask的參數

Flask 是一個非常靈活且短小精干的web框架 , 那么靈活性從什么地方體現呢?

有一個神奇的東西叫 Flask配置 , 這個東西怎么用呢? 它能給我們帶來怎么樣的方便呢?

首先展示一下:

from flask import Flask

app = Flask(__name__)  # type:Flask
app.config["DEBUG"] = True

if __name__ == '__main__':
    app.run()
View Code

這句 app.config["DEBUG"] = True 可以實現的功能可刺激了

代碼只要發生改動,自動重啟Flask程序(app.run)

在控制台打印的信息非常全面

以上兩個功能就是傳說中的 DEBUG 模式(調試模式)

 

Flask的配置就是在 app.config 中添加一個鍵值對,但是你存進去的鍵必須是config中應該存在的,如果不再存在的話,它會默認無用,就這么放着

config中有多少有用的key 呢?

{
    'DEBUG': False,  # 是否開啟Debug模式
    'TESTING': False,  # 是否開啟測試模式
    'PROPAGATE_EXCEPTIONS': None,  # 異常傳播(是否在控制台打印LOG) 當Debug或者testing開啟后,自動為True
    'PRESERVE_CONTEXT_ON_EXCEPTION': None,  # 一兩句話說不清楚,一般不用它
    'SECRET_KEY': None,  # 之前遇到過,在啟用Session的時候,一定要有它
    'PERMANENT_SESSION_LIFETIME': 31,  # days , Session的生命周期(天)默認31天
    'USE_X_SENDFILE': False,  # 是否棄用 x_sendfile
    'LOGGER_NAME': None,  # 日志記錄器的名稱
    'LOGGER_HANDLER_POLICY': 'always',
    'SERVER_NAME': None,  # 服務訪問域名
    'APPLICATION_ROOT': None,  # 項目的完整路徑
    'SESSION_COOKIE_NAME': 'session',  # 在cookies中存放session加密字符串的名字
    'SESSION_COOKIE_DOMAIN': None,  # 在哪個域名下會產生session記錄在cookies中
    'SESSION_COOKIE_PATH': None,  # cookies的路徑
    'SESSION_COOKIE_HTTPONLY': True,  # 控制 cookie 是否應被設置 httponly 的標志,
    'SESSION_COOKIE_SECURE': False,  # 控制 cookie 是否應被設置安全標志
    'SESSION_REFRESH_EACH_REQUEST': True,  # 這個標志控制永久會話如何刷新
    'MAX_CONTENT_LENGTH': None,  # 如果設置為字節數, Flask 會拒絕內容長度大於此值的請求進入,並返回一個 413 狀態碼
    'SEND_FILE_MAX_AGE_DEFAULT': 12,  # hours 默認緩存控制的最大期限
    'TRAP_BAD_REQUEST_ERRORS': False,
    # 如果這個值被設置為 True ,Flask不會執行 HTTP 異常的錯誤處理,而是像對待其它異常一樣,
    # 通過異常棧讓它冒泡地拋出。這對於需要找出 HTTP 異常源頭的可怕調試情形是有用的。
    'TRAP_HTTP_EXCEPTIONS': False,
    # Werkzeug 處理請求中的特定數據的內部數據結構會拋出同樣也是“錯誤的請求”異常的特殊的 key errors 。
    # 同樣地,為了保持一致,許多操作可以顯式地拋出 BadRequest 異常。
    # 因為在調試中,你希望准確地找出異常的原因,這個設置用於在這些情形下調試。
    # 如果這個值被設置為 True ,你只會得到常規的回溯。
    'EXPLAIN_TEMPLATE_LOADING': False,
    'PREFERRED_URL_SCHEME': 'http',  # 生成URL的時候如果沒有可用的 URL 模式話將使用這個值
    'JSON_AS_ASCII': True,
    # 默認情況下 Flask 使用 ascii 編碼來序列化對象。如果這個值被設置為 False ,
    # Flask不會將其編碼為 ASCII,並且按原樣輸出,返回它的 unicode 字符串。
    # 比如 jsonfiy 會自動地采用 utf-8 來編碼它然后才進行傳輸。
    'JSON_SORT_KEYS': True,
    #默認情況下 Flask 按照 JSON 對象的鍵的順序來序來序列化它。
    # 這樣做是為了確保鍵的順序不會受到字典的哈希種子的影響,從而返回的值每次都是一致的,不會造成無用的額外 HTTP 緩存。
    # 你可以通過修改這個配置的值來覆蓋默認的操作。但這是不被推薦的做法因為這個默認的行為可能會給你在性能的代價上帶來改善。
    'JSONIFY_PRETTYPRINT_REGULAR': True,
    'JSONIFY_MIMETYPE': 'application/json',
    'TEMPLATES_AUTO_RELOAD': None,
}
View Code

以上這些Key,都可以被改寫,當然他們也都是有默認值存在的,如果沒有特殊情況,不要改寫它的默認值

修改配置的方式

修改配置的方式大約是兩種

1.直接對app.config進行修改

app.config["DEBUG"] = True

2.使用類的方式導入

首先要有一個settings.py的文件

class FlaskSetting(object):
    DEBUG = True

 

然后我們在Flask的啟動文件中就可以這么寫

from flask import Flask
# 導入自定義配置文件的配置類
from settings import FlaskSetting

app = Flask(__name__)
# 應用配置類
app.config.from_object(FlaskSetting)

if __name__ == '__main__':
    app.run()
View Code

這叫做類導入配置

實例化配置

這是針對一個已經實例化的app進行的配置

那么在Flask實例化的時候,傳遞的參數是什么鬼呢?

其實可以理解為對Flask實例進行的初始配置,這里面的參數是非常好理解,注意關鍵字是非常非常非常好理解

static_folder = 'static',  # 靜態文件目錄的路徑 默認當前項目中的static目錄
static_host = None,  # 遠程靜態文件所用的Host地址,默認為空
static_url_path = None,  # 靜態文件目錄的url路徑 默認不寫是與static_folder同名,遠程靜態文件時復用
# host_matching是否開啟host主機位匹配,是要與static_host一起使用,如果配置了static_host, 則必須賦值為True
# 這里要說明一下,@app.route("/",host="localhost:5000") 就必須要這樣寫
# host="localhost:5000" 如果主機頭不是 localhost:5000 則無法通過當前的路由
host_matching = False,  # 如果不是特別需要的話,慎用,否則所有的route 都需要host=""的參數
subdomain_matching = False,  # 理論上來說是用來限制SERVER_NAME子域名的,但是目前還沒有感覺出來區別在哪里
template_folder = 'templates'  # template模板目錄, 默認當前項目中的 templates 目錄
instance_path = None,  # 指向另一個Flask實例的路徑
instance_relative_config = False  # 是否加載另一個實例的配置
root_path = None  # 主模塊所在的目錄的絕對路徑,默認項目目錄
View Code

這里面,我們常用的參數有

static_folder = 'static',  # 靜態文件目錄的路徑 默認當前項目中的static目錄
static_url_path = None,  # 靜態文件目錄的url路徑 默認不寫是與static_folder同名,遠程靜態文件時復用
template_folder = 'templates'  # template模板目錄, 默認當前項目中的 templates 目錄

記住這些就好了,一般的項目中,只修改這些參數

 

template_folder 

昨天已經講到了template_folder,它是用來指定模板目錄的,默認是templates

注意:如果設置template_folder = 'templates',這里面的templates它是相對路徑!

假設py文件和templates不在同一目錄下,比如這樣:

./
├── bin
│   └── start.py
└── templates
    └── home.html

那么start.py使用模板時,應該這么設置  template_folder = '../templates'

完整代碼如下:

from flask import Flask,render_template

app = Flask(__name__,template_folder="../templates")

@app.route("/")
def index():
    return render_template("home.html")

if __name__ == '__main__':
    app.run()
View Code

 

如果找不到模板文件,會提示

 

static_folder

靜態文件目錄的路徑 默認當前項目中的static目錄

目錄結構如下:

./
├── bin
│   └── start.py
├── static
│   └── meizi.jpg
└── templates
    └── home.html

 

start.py

from flask import Flask,render_template

app = Flask(__name__,template_folder="../templates",static_folder="../static")

@app.route("/")
def index():
    return render_template("home.html")

if __name__ == '__main__':
    app.run()
View Code

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>古裝美女</h3>
<img src="/static/meizi.jpg" alt="">
</body>
</html>
View Code

meizi.jpg 這是一個妹子圖片,自己百度搜索一下

 

重啟flask程序,效果如下:

查看網絡,圖片的實際路徑是:

http://127.0.0.1:5000/static/meizi.jpg

 

static_url_path

靜態文件目錄的url路徑 默認不寫是與static_folder同名,遠程靜態文件時復用

怎么知道static_url_path和static_folder默認同名呢?

修改start.py,打印變量

from flask import Flask,render_template

app = Flask(__name__,template_folder="../templates",static_folder="../static")

@app.route("/")
def index():
    print(app.static_folder)
    print(app.static_url_path)
    return render_template("home.html")

if __name__ == '__main__':
    app.run()
View Code

刷新頁面,查看Pycharm控制台輸出:

/static
/static

這2個確實是一樣的!

 

static_url_path變量是可以修改的,但是和前端保持一致

修改start.py

from flask import Flask,render_template

app = Flask(__name__,template_folder="../templates",static_folder="../static",static_url_path="/app")

@app.route("/")
def index():
    print(app.static_folder)
    print(app.static_url_path)
    return render_template("home.html")

if __name__ == '__main__':
    app.run()
View Code

修改home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>古裝美女</h3>
<img src="/app/meizi.jpg" alt="">
</body>
</html>
View Code

重啟flask,刷新頁面,效果同上!

 

注意:如果使用藍圖,並做了代碼拆分。靜態目錄名不能是static,模板目錄不能是templates。必須改名!!!

必須指定3個參數

template_folder="../templates",static_folder="../statics",static_url_path="/statics" 

否則會出現,模板無法渲染。css訪問出現404的問題!

視圖渲染時,使用

return render_template("index.html")

模板引用css時,使用

<link rel="stylesheet" href="/statics/bootstrap.min.css">

 

本文參考:

https://www.cnblogs.com/DragonFire/p/9260299.html

三、藍圖(BluePrint)

什么是藍圖

Flask中提供了藍圖,專門用作Flask的模塊化。對於藍圖,可以看官方介紹,這里翻譯過來的:

Flask使用藍圖的概念來制作應用程序組件和支持應用程序內部或跨應用程序的通用模式。藍圖可以大大簡化大型應用程序的工作方式,並為Flask擴展提供了在應用程序上注冊操作的中心手段。Blueprint對象的工作方式與Flask應用程序對象類似,但實際上它不是一個應用程序。相反,它是如何構造或擴展應用程序的藍圖。

總之,藍圖可以使我們的程序更加模塊化,不同功能的路由可以放在不同的模塊下,最后集中到啟動類中

 

藍圖,聽起來就是一個很宏偉的東西

在Flask中的藍圖 blueprint 也是非常宏偉的

它的作用就是將 功能 與 主服務 分開怎么理解呢?

比如說,你有一個客戶管理系統,最開始的時候,只有一個查看客戶列表的功能,后來你又加入了一個添加客戶的功能(add_user)模塊, 然后又加入了一個刪除客戶的功能(del_user)模塊,然后又加入了一個修改客戶的功能(up_user)模塊,在這個系統中,就可以將

查看客戶,修改客戶,添加客戶,刪除客戶的四個功能做成藍圖加入到客戶管理系統中,本篇最后會做一個這樣的例子,但是首先我們要搞清楚什么是藍圖 blueprint

初識Flask藍圖

1.初識Flask藍圖(blueprint)

創建一個項目然后將目錄結構做成:

./
├── manager.py
└── student_view
    └── s_view.py

注意:要手動創建目錄student_view,並在此目錄下創建s_view.py

 

s_view.py

from flask import Blueprint  # 導入 Flask 中的藍圖 Blueprint 模塊

sv = Blueprint("sv", __name__)  # 實例化一個藍圖(Blueprint)對象


@sv.route("/svlist")  # 這里添加路由和視圖函數的時候與在Flask對象中添加是一樣的
def view_list():
    return "svlist_view_list"
View Code

 

manager.py

from flask import Flask

# 導入此前寫好的藍圖模塊
from student_view import s_view

app = Flask(__name__)  # type:Flask

# 在Flask對象中注冊藍圖模塊中的藍圖對象 s_view 中的 sv
app.register_blueprint(s_view.sv)

if __name__ == '__main__':
    app.run("0.0.0.0",5000)
    # 現在Flask對象中並沒有寫任何的路由和視圖函數
View Code

開啟服務,然后訪問 http://127.0.0.1:5000/svlist 查看結果

 

很明顯,我們沒有在Flask對象中添加路由,但是我們注冊了有路由和視圖函數的sv藍圖對象

理解藍圖

2.如何理解藍圖呢?

其實我們可以理解成一個沒有run方法的Flask對象,這個理論雖然有很多的漏洞,但是對於剛接觸藍圖的你來說,就這么樣理解,沒有錯

下面來看一下,在實例化藍圖的時候可以傳遞的參數都有什么,你就能完全理解了

目錄結構:

./
├── manager.py
├── student_view
│   └── s_view.py
├── sv_static
│   └── meizi.jpg
└── sv_template
    └── svlist.html

 

s_view.py

from flask import Blueprint  # 導入 Flask 中的藍圖 Blueprint 模塊
from flask import render_template

sv = Blueprint("sv",
               __name__,
               # 這里是相對路徑,要加../
               template_folder="../sv_template",  # 每個藍圖都可以為自己獨立出一套template模板文件夾,如果不寫則共享項目目錄中的templates
               static_folder="../sv_static"  # 靜態文件目錄也是可以獨立出來的
               )  # 實例化一個藍圖(Blueprint)對象


@sv.route("/svlist")
def view_list():
    return render_template("svlist.html")
View Code

 

svlist.html 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>Hello ! I am sv_template</p>
    <img src="/sv_static/meizi.jpg">
</body>
</html>
View Code

manager.py的代碼,不需要改動。

重啟flask,刷新頁面:

妹子,還是那個妹子

 

從這個例子中我們總結出:

Blueprint 其實可以理解為一個了沒有run方法的 Flask 對象

只要Blueprint被 Flask 注冊了,就一定會生效

坑來了!坑來了!

藍圖內部的視圖函數及route不要出現重復,否則~你們自己試試吧

 

增刪改查用戶

3.使用藍圖,做一個增刪改查用戶

要有一個文件存放我們的原始數據

student_data.py 文件中的內容:

STUDENT = [
    {'id':1,'name': '韓雪', 'age': 24, 'gender': ''},
    {'id':2,'name': '舒暢', 'age': 23, 'gender': ''},
    {'id':3,'name': '唐嫣', 'age': 25, 'gender': ''}
]

然后我們根據以上內容進行增刪改查

web應用搭建

3.1 使用藍圖進行web應用搭建:

目錄結構如下:

./
├── manager.py
├── student
│   └── __init__.py
└── student_data.py

__init__.py 文件中的內容:

from flask import Flask


def create_app():
    app = Flask(__name__)

    return app
View Code

這個文件我們會修改函數 create_app中的代碼

 

manager.py 文件中的內容

from student import create_app

flask_app = create_app()

if __name__ == '__main__':
    flask_app.run("0.0.0.0",5000,debug=True)
View Code

通過這種方式啟動 Flask 程序

查看學生信息

3.2 使用Flask藍圖,查看學生信息

項目結構如下:

./
├── html
│   └── s_list.html
├── manager.py
├── student
│   └── __init__.py
├── student_data.py
└── student_select
    └── stu_select.py

s_list.html 文件中的內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>學生列表</title>
</head>
<body>
<table border="3xp">
    <thead>
    <tr>
        <td>ID</td>
        <td>name</td>
        <td>age</td>
        <td>gender</td>
        <td>options</td>
    </tr>
    </thead>
    <tbody>
    {% for foo in student %}
        <tr>
            <td>{{ foo.id }}</td>
            <td>{{ foo["name"] }}</td>
            <td>{{ foo.get("age") }}</td>
            <td>{{ foo.gender }}</td>
            <td><a href="/s_update/{{ foo.id }}">修改</a> | <a href="/s_del?id={{ foo.id }}">刪除</a></td>
        </tr>
    {% endfor %}
    </tbody>
</table>
<a href="/s_add"> 添加學生 </a>
</body>
</html>
View Code

 

stu_select.py 文件中的內容:

from flask import Blueprint
from flask import render_template
from student_data import STUDENT

ss_blueprint = Blueprint("ss_b", __name__, template_folder="../html")


@ss_blueprint.route("/s_list")
def s_list():
    return render_template("s_list.html", student=STUDENT)
View Code

 

student/__init__.py 文件中的內容:

from flask import Flask
from student_select import stu_select


def create_app():
    app = Flask(__name__)

    app.register_blueprint(stu_select.ss_blueprint)

    return app
View Code

 

趕緊運行一下manager.py 來訪問一下,我們的成果

 

什么鏈接都不要點,因為點啥都不好使,之后咱們一個一個的做

添加一個學生

3.3. 使用Flask藍圖,添加一個學生

增加一個目錄,結構如下:

./
├── html
│   ├── s_add.html
│   └── s_list.html
├── manager.py
├── student
│   └── __init__.py
├── student_add
│   └── stu_add.py
├── student_data.py
└── student_select
    └── stu_select.py

 

s_add.html 文件中的內容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>學生列表</title>
</head>
<body>
<form method="post">
    ID:<input type="text" name="id"> <br>
    姓名:<input type="text" name="name"><br>
    年齡:<input type="text" name="age"><br>
    性別:<input type="text" name="gender"><br>
    <input type="submit" value="添加學生">
</form>

</body>
</html>
View Code

 

stu_add.py 文件中的內容

from flask import Blueprint
from flask import redirect
from flask import request
from flask import render_template
from student_data import STUDENT

s_add = Blueprint("s_add", __name__, template_folder="html", static_folder="static")  # type:Blueprint


@s_add.route("/s_add", methods=["GET", "POST"])
def s_add_view():
    if request.method == "POST":
        stu_dic = {
            "id": request.form["id"],
            "name": request.form["name"],
            "age": request.form["age"],
            "gender": request.form["gender"]
        }

        STUDENT.append(stu_dic)

        return redirect("/s_list")

    return render_template("s_add.html")
View Code

 

這里面我們讓他添加完一個學生,就返回到s_list查看學生列表

student/__init__.py 文件中的內容

from flask import Flask
from student_select import stu_select
from student_add import stu_add


def create_app():
    app = Flask(__name__)

    app.register_blueprint(stu_select.ss_blueprint)
    app.register_blueprint(stu_add.s_add)

    return app
View Code

 

重啟flask服務,點擊添加學生

 

如果你要是重新啟動服務了,那么你剛剛添加的學生信息就沒有了

添加完成之后

 

添加學生的Blueprint已經做完了

修改學生信息

3.4. 使用Flask藍圖,修改學生信息

增加一個目錄,結構如下:

./
├── html
│   ├── s_add.html
│   ├── s_list.html
│   └── s_update.html
├── manager.py
├── student
│   └── __init__.py
├── student_add
│   └── stu_add.py
├── student_data.py
├── student_select
│   └── stu_select.py
└── student_update
    └── stu_update.py

 

s_update.html 文件中的內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>學生列表</title>
</head>
<body>
<form method="post">
    <input type="text" name="id" hidden value="{{ student.id }}"><br>
    姓名:<input type="text" name="name" value="{{ student.name }}"><br>
    年齡:<input type="text" name="age" value="{{ student.age }}"><br>
    性別:<input type="text" name="gender" value="{{ student.gender }}"><br>
    <input type="submit" value="修改信息">
</form>

</body>
</html>
View Code

 

stu_update.py 文件中的內容:

from flask import Blueprint
from flask import render_template
from flask import redirect
from flask import request
from student_data import STUDENT

s_update = Blueprint("s_update", __name__, template_folder="html", static_folder="static")


@s_update.route("/s_update/<int:nid>",methods=["GET","POST"])
def s_update_view(nid):
    if request.method == "POST":
        stu_id = int(request.form["id"])
        stu_dic = {
            "id": stu_id,
            "name": request.form["name"],
            "age": request.form["age"],
            "gender": request.form["gender"]
        }

        for index,stu in enumerate(STUDENT):
            if stu["id"] == stu_id:
                STUDENT[index] = stu_dic

        return redirect("/s_list")

    for stu in STUDENT:
        if stu["id"] == nid :
            return render_template("s_update.html", student=stu)

    return render_template("s_update.html", student="")
View Code

 

student/__init__.py 文件中的內容:

from flask import Flask
from student_select import stu_select
from student_add import stu_add
from student_update import stu_update


def create_app():
    app = Flask(__name__)  # type:Flask

    app.register_blueprint(stu_select.ss_blueprint)
    app.register_blueprint(stu_add.s_add)
    app.register_blueprint(stu_update.s_update)

    return app
View Code

 

重啟flask,刷新網頁。點擊一條記錄,並修改

 

修改年齡

 點擊修改信息,效果如下:

 

 

修改的功能也已經做完了,刪除功能也是一樣的。

 

刪除學生

3.4. 使用Flask藍圖,刪除學生信息

增加一個目錄,結構如下:

./
├── html
│   ├── s_add.html
│   ├── s_list.html
│   └── s_update.html
├── manager.py
├── student
│   └── __init__.py
├── student_add
│   └── stu_add.py
├── student_data.py
├── student_delete
│   └── stu_delete.py
├── student_select
│   └── stu_select.py
└── student_update
    └── stu_update.py

注意:刪除不需要html文件

 

修改s_list.html,這里的刪除鏈接有問題

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>學生列表</title>
</head>
<body>
<table border="3xp">
    <thead>
    <tr>
        <td>ID</td>
        <td>name</td>
        <td>age</td>
        <td>gender</td>
        <td>options</td>
    </tr>
    </thead>
    <tbody>
    {% for foo in student %}
        <tr>
            <td>{{ foo.id }}</td>
            <td>{{ foo["name"] }}</td>
            <td>{{ foo.get("age") }}</td>
            <td>{{ foo.gender }}</td>
            <td><a href="/s_update/{{ foo.id }}">修改</a> | <a href="/s_delete/{{ foo.id }}">刪除</a></td>
        </tr>
    {% endfor %}
    </tbody>
</table>
<a href="/s_add"> 添加學生 </a>
</body>
</html>
View Code

 

stu_delete.py 文件中的內容:

from flask import Blueprint
from flask import render_template
from flask import redirect
from flask import request
from student_data import STUDENT

s_delete = Blueprint("s_delete", __name__, template_folder="html", static_folder="static")


@s_delete.route("/s_delete/<int:nid>",methods=["GET","POST"])
def s_delete_view(nid):
    for stu in STUDENT:
        if stu["id"] == nid :
            STUDENT.remove(stu)  # 列表移除key
            return redirect("/s_list")

    return redirect("/s_list")
View Code

 

student/__init__.py 文件中的內容,注冊藍圖

from flask import Flask
from student_select import stu_select
from student_add import stu_add
from student_update import stu_update
from student_delete import stu_delete


def create_app():
    app = Flask(__name__)

    app.register_blueprint(stu_select.ss_blueprint)
    app.register_blueprint(stu_add.s_add)
    app.register_blueprint(stu_update.s_update)
    app.register_blueprint(stu_delete.s_delete)

    return app
View Code

 

重啟flask,測試效果:

 

增刪改查,功能全部完結了!各位看官,有時間的話,可以使用pymysql實現!

建表以及插入數據

# 創建數據庫
CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL COMMENT '用戶名',
  `age` int(11) DEFAULT NULL COMMENT '年齡',
  `gender` enum('','') DEFAULT '' COMMENT '性別',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

# 插入3條數據
INSERT INTO `student` (`id`, `name`, `age`, `gender`) VALUES ('1', '韓雪', '24', '');
INSERT INTO `student` (`id`, `name`, `age`, `gender`) VALUES ('2', '舒暢', '23', '');
INSERT INTO `student` (`id`, `name`, `age`, `gender`) VALUES ('3', '唐嫣', '25', '');
View Code

 

完整代碼,請參考:

鏈接:https://pan.baidu.com/s/1wOOM2xk6YDZRo5HljgszpQ 密碼:viel

 

本文參考:

https://www.cnblogs.com/DragonFire/p/9264381.html

四、before_request after_request

Flask我們已經學習很多基礎知識了,現在有一個問題

我們現在有一個 Flask 程序其中有3個路由和視圖函數,如下:

from flask import Flask

app = Flask(__name__)


@app.route("/login")
def login():
    return "Login"

@app.route("/index")
def index():
    return "Index"

@app.route("/home")
def home():
    return "Home"

if __name__ == '__main__':
    app.run("0.0.0.0", 5000)
View Code

啟動flask,訪問home頁面

 

現在要求是,如果登陸了,就可以訪問 index 和 home 頁面,如果沒登錄就跳轉到 login 登錄

要怎么解決呢, session 對, 用 session 除了 Login 函數之外的所有函數里面全校驗 session 是否登錄了

太麻煩了,現在咱們只有3個函數,如果成百上千個怎么整啊

 

裝飾器,對沒錯,裝飾器是一個很好的方案,但是啊,我現在還是成敗上千個函數,我要在每一個函數定義的時候加上@裝飾器,還是很麻煩

那么就引出了我們要學習的第一個知識點:

@app.before_request

from flask import Flask,request,redirect,session

app = Flask(__name__)
app.secret_key = "DragonFire"


@app.before_request
def is_login():  # 判斷是否登錄
    # 白名單設置,判斷為登錄頁面時
    if request.path == "/login":
        # 跳過處理
        return None
    # 判斷session是不存在時
    if not session.get("user"):
        # 重定向到登錄頁面
        return redirect("/login")

@app.route("/login")
def login():
    return "Login"

@app.route("/index")
def index():
    return "Index"

@app.route("/home")
def home():
    return "Home"

if __name__ == '__main__':
    app.run("0.0.0.0", 5000)
View Code

@app.before_request 也是一個裝飾器,他所裝飾的函數,都會在請求進入視圖函數之前執行

request.path 是來讀取當前的url地址如果是 /login 就允許直接通過 return None 你可以理解成通過放行

校驗session中是否有user 如果沒有的話,證明沒有登錄,所以毫不留情的 redirect("/login") 跳轉登錄頁面

還有一個要提的 @app.before_first_request 它與 @app.before_request 極為相似或者說是一模一樣,只不過它只會被執行一次

 

@app.before_request修飾器在開發中用處非常大,比如判斷某個ip是否有惡意訪問行為,從而進行攔截等操作

 

重啟flask,再次訪問home頁面,效果如下:

打開瀏覽器工具,查看網絡

 

@app.after_request 

 2. @app.after_request 在響應(response)之前做出響應

from flask import Flask,request,redirect,session

app = Flask(__name__)
app.secret_key = "DragonFire"


@app.before_request
def is_login():  # 判斷是否登錄
    # 白名單設置,判斷為登錄頁面時
    if request.path == "/login":
        # 跳過處理
        return None
    # 判斷session是不存在時
    if not session.get("user"):
        # 重定向到登錄頁面
        return redirect("/login")

@app.after_request
def foot_log(environ):  # 記錄訪問日志
    print(environ)  # 響應信息
    # 判斷請求路徑不是登錄頁面
    if request.path != "/login":
        # 打印訪問路徑
        print("有客人訪問了",request.path)

    return environ

@app.route("/login",methods=["POST","GET"])
def login():
    if request.method == "GET":
        return "Login"

    user = request.form["username"]  # form表單獲取
    pwd = request.form["password"]  # form表單獲取
    # 判斷form表示數據和 后台數據庫匹配
    # models.UserInfo.objects.filter(username=user,password=pwd).first()
    if user == 'xiao' and pwd == '123':
        # 設置session
        session["user"] = user
        # 跳轉首頁
        return redirect("/index")


@app.route("/index")
def index():
    return "Index"

@app.route("/home")
def home():
    return "Home"

if __name__ == '__main__':
    app.run("0.0.0.0", 5000)
View Code

 

@app.after_request修飾器是在用戶請求得到函數響應后被執行,不過需要注意的是這個執行是在函數返回數據前被調用,即請求已經被app.route修飾的函數響應過了,已經形成了response,但還未返回給用戶的時候,調用的。

它可以做訪問統計,很少應用,但是要了解有這么個東西

 

重啟flask,訪問home頁面,效果同上!它還是會跳轉到登錄頁面

查看Pycharm控制台輸出:

<Response 219 bytes [302 FOUND]>
有客人訪問了 /home

 使用postman發送post請求

點擊發送,查看返回結果

從上圖可以看出,驗證通過了!跳轉到首頁

 

可能有人會有疑問,咦?@app.before_request不是裝飾器嗎?為什么沒有在home視圖函數中應用,卻生效了呢?
應該這樣才對啊

@app.route("/home")
@app.before_request
def home():
    return "Home"

NoNoNo!@app.before_request是一個全局裝飾器,它是針對所有視圖函數的。只要定義了@app.before_request,那么每一個視圖函數都會應用。同理,@app.after_request也是全局裝飾器!

你可以把@app.before_request理解為django的中間件,請求到達視圖函數之前,先走中間件!

 

如果還是不明白,看下面的流程圖

流程圖

解釋:

is_login()函數被@app.before_request修飾以后,每一次請求到來后,都會先進入函數is_login()中,如上代碼,獲取請求的路徑以及session。如果是登錄頁面,不處理。如果session中user不存在,跳轉到登錄頁面。當session中user存在時,請求才會正常進入到app.route修飾的函數中響應,如果有多個函數被@app.before_request修飾了,那么這些函數會被依次執行。


比如這樣:

@app.before_request
    def malicious_ip(): # 判斷惡意IP
    pass

@app.before_request
    def xss(): # 判斷xss攻擊
    pass

@app.before_request
    def is_login(): # 判斷登錄
    pass

@app.before_request修飾器在開發中用處非常大,比如判斷某個ip是否有惡意訪問行為,從而進行攔截等操作。


此外同理,app.after_request修飾器是在用戶請求得到函數響應后被執行,不過需要注意的是這個執行是在函數返回數據前被調用,即請求已經被app.route修飾的函數響應過了,已經形成了response,但還未返回給用戶的時候,調用的。

 

本文參考:

https://www.cnblogs.com/DragonFire/p/9269303.html

 

今日總結:

1.flask路由
    endpoint 反向url標簽 
    url_for 通過endpoint反向生成url
    methods=[] 允許進入視圖函數的請求方式,默認GET
    strict_slashes 是否嚴格要求URL地址 /login/ /login
    redirect_to 跳轉地址,重定向地址
    動態URL參數 /index/1 /index/<int:id>  def index(id)
    
2.Flask實例化配置
    template_folder 指定模板路徑
    static_folder 指定靜態文件路徑
    static_url_path 靜態文件路徑訪問url

3.app對象配置:
    app.config.from_object(Class)
    DEBUG # 開啟代碼調試模式(開發)
    SECRET_KEY # 用到session時必須添加
    
4.藍圖:
    Blueprint 相當於是一個不能夠被run的flask對象
    Blueprint("blue",__name__,template_folder,static_folder,static_url_path)
    可以為藍圖增加獨立的模板和靜態目錄
    為藍圖增加路由
    讓flask實例 注冊藍圖 register_blueprint(藍圖)
    功能和主程序分離,注冊
    
5. send_file jsonify
    send_file() # 打開文件並返回,並加入返回頭
    jsonify # 將字典json后,加入返回頭applction/json


6.特殊裝飾器:
    before_request: 請求進入視圖函數之前執行
    after_request: 響應返回前端之前執行
    errorhandler(404): 錯誤響應
    before_first_request: 第一次訪問時,請求進入視圖函數之前執行
    
    be1 - be2 - af2 - af1
    be1 - af2 - af1


7.閃現 flash:
    
    flash("內容","標簽")
    get_flashed_messages()
    get_flashed_messages(category_filter=["標簽"])
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM