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之外,其餘視圖函數都要登陸才行!css

使用session判斷!html

原始代碼前端

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

 

使用裝飾器mysql

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_listweb

http://127.0.0.1:5000/student_list

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

輸入正確的用戶名和密碼數據庫

提交以後,跳轉頁面django

 

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

由於使用裝飾器以後,視圖函數都是inner,因此flask沒法區分,路由到底指向哪個視圖函數。flask

啓動flask以後,會直接報錯。endpoint參數,是給url起別名,惟一標識。能夠作url反向解析!

 

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

可是不推薦使用。由於定義視圖函數,自己就應該定義endpoint參數

 

1、路由系統

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位數字

 

本文參考:

http://www.javashuo.com/article/p-efehdfyj-mx.html

2、實例化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">

 

本文參考:

http://www.javashuo.com/article/p-qdsnjfmg-cd.html

3、藍圖(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

 

本文參考:

http://www.javashuo.com/article/p-ahcaxosm-kq.html

4、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,但還未返回給用戶的時候,調用的。

 

本文參考:

http://www.javashuo.com/article/p-pruhuxdw-dh.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
相關文章
相關標籤/搜索