flask請求上下文 + 上下文鉤子 + 單元測試

一、flask請求上下文


原文檔:
flask 源碼解析:上下文


1. flask請求上下文介紹


  • Flask中有兩種上下文,程序上下文(application context)和請求上下文(request context)
    在這裏插入圖片描述

文章中 承上啓下,

程序中: 根據已經寫完,完成的代碼,以及即將要完成的代碼,去決定當前時刻要完成的或者要是使用的變量和方法

當請求進入時,flask會自動激活請求上下文,這時可以使用request和session變量。當請求上下文被激活時,程序上下文也被自動激活。

除了上面四個上下文變量,依賴上下文的還有url_for()和jsonify()函數,所以只能在視圖函數中使用它們。其中jsonify()函數內部調用中使用了current_app變量,url_for()則需要依賴請求上下文才可以正常運行。


2. flask請求上下文的組成結構


① request context:


  • request 負責請求,獲取請求方式,獲取請求數據
    • 視圖函數需要上下文信息,flask將請求報文封裝在request對象中,但是在視圖函數中,並沒有把它傳進視圖函數,而是直接從Flask導入一個全局的request對象,然後在視圖函數裏直接調用request的屬性獲取數據。爲什麼在處理請求時,視圖函數裏的request會自動包含對應請求的數據呢?因爲flask在每個請求產生後自動激活當前請求的上下文,激活請求上下文後,request被臨時設爲全局可訪問。當每個請求結束後,flask就銷燬對應的請求上下文。我們在前面說request是全局對象,但這裏的全局並不是實際意義上的全局。我們把這些變量理解爲動態的全局變量。
  • session 負責session設置,獲取,刪除

② app lication context 又演化出來兩個變量 :


  • current_app (當前app實例)
    • 在不同的視圖函數中,request對象都表示和視圖函數對應的請求,就是當前請求(current request)。程序會有多個程序實例的情況,爲了能獲取對應的程序實例,而不是固定的某一個程序實例,我們就需要使用current_app變量。
  • g(類似於全局變量)
    • g存儲在程序上下文中,而程序上下文會隨着每一個請求的進入而激活,隨着每一個請求的處理完畢而銷燬,所以每次請求都會重設這個g值。

        我們通常會使用它結合鉤子來保存每個請求處理前所需要的全局變量。
      

3. g對象的使用


類似於全局變量

from flask import Flask,Blueprint,request
from flask import current_app,g
g_views = Blueprint("goods_views",__name__)


@g_views.before_request
def demo01():
    user_name = request.args.get("username")
    g.user_name = user_name


def outer(func):
    def inner():
        ##  通過request 獲取了用戶的username
        ## 查詢到了用戶的  user_id
        user_id = 1
        g.user_id = user_id

        return func()
    return inner

@g_views.route("/index/")
@outer
def index():
    ###  也需要獲取用戶id
    ###    request 獲取到用戶的username
    ##    查詢用戶   user_id
    user_id = g.user_id
    user_name = g.user_name
    print (user_id)
    print (user_name)
    mymd5()
    return "goods views index"


def mymd5():
    ## md5   需要密鑰
    ##從app的配置文件中 取到 密鑰
    ## 第一種 導入app  從app中取配置信息    app.config["SECRET_KEY"]

    #
    config = current_app.config
    value = config.get("SECRET_KEY")
    print(value)


    return "我是加密後的結果"

在這裏插入圖片描述


4. current_app的使用


  • 代理人
  • 經常用在多文件編程,多藍圖,多視圖,多模塊,當某一些模塊中需要使用app的功能,不方便直接拿到app,所以需要代理人
from flask import Flask,Blueprint
from flask import current_app
g_views = Blueprint("goods_views",__name__)

@g_views.route("/index/")
def index():
    mymd5()
    return "goods views index"


def mymd5():
    ## md5   需要密鑰
    ##從app的配置文件中 取到 密鑰
    ## 第一種 導入app  從app中取配置信息    app.config["SECRET_KEY"]

    # current_app使用方法
    config = current_app.config
    value = config.get("SECRET_KEY")
    print(value)


    return "我是加密後的結果"

二、上下文鉤子(回調函數)


1. 鉤子的介紹


flask爲上下文提供了一個teardown_appcontext鉤子,使用它註冊的毀掉函數會在程序上下文被銷燬時調用,通常也在請求上下文被銷燬時調用,比如你需要在每個請求處理結束後銷燬數據庫連接:

@app.teardown_appcontext

def teardown_db(exception):

db.close()

app.reardown_appcontext裝飾器註冊的回調函數需要接收異常對象作爲參數,當請求被正常處理時這個參數將是None,這個函數的返回值將被忽略


2. flask請求鉤子的運行過程


① Django中的中間件


上下文鉤子類似於django的中間件

django中間件中有以下幾個方法:

  • process_request
  • process_view
  • view
  • process_response
  • process_template_response
  • process_exception

② flask請求鉤子


在干預請求和響應之間,flask中提供了四個這樣的鉤子:

  • before_first_request
    • 請求到達視圖之前
    • 當項目被啓動之後,第一個請求到達前,執行(只執行一次)
  • before_request
    • 請求到達視圖之前
    • 每次請求到達都被執行
      進入視圖 return
  • after_request
    • 如果試圖中沒有拋出異常,在每次請求之後被執行
    • 接收參數: 視圖處理之後返回的響應response
  • teardown_request
    • 每次的請求之後執行
    • 接收一個參數,參數是錯誤異常,
    • 如果有錯誤異常,然後拋出異常

3. flask鉤子的使用


flask中使用鉤子,需要使用裝飾器去使用

具體如下:

from flask import Flask
app = Flask(__name__)

####
@app.before_first_request  #### 當第一個請求到達的時候執行
def demo01():
    """
    demo01 這個函數:  做一些項目的初始化配製
    :return:
    """

    print ("before_first_request")
@app.before_request
def demo02():
    ### 獲取用戶的信息
    ###  獲取用戶的session   cookie
    ### 校驗用戶的身份
    print("before_request")
    print("before_request")
    userid = request.args.get("id")
    if not userid:
        return "缺少請求信息"

@app.after_request
def demo03(response):
    print ("after_request")
    ##  構建返回的結構
    return response
@app.teardown_request
def demo04(response):
    print (response)
    print("teardown_request")

@app.route("/index/")
def index():
    ####
    1/0
    return "index"

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


4. 請求鉤子的作用


  • 我們可以利用鉤子來達到想要的功能,利用他們在請求到達前、時、後來攜帶我們想要的信息或功能
    在這裏插入圖片描述
  • 請求報錯,response攜帶報錯信息
    在這裏插入圖片描述
  • flask提供abort
    abort(404)修改報錯狀態碼,如500-----》404

三、單元測試


1. 單元測試的介紹


將一個功能的最小模塊進行測試,測試的目的:是否滿足預期

登錄功能:

  • 用戶輸入的用戶名 類型 ,長度,是否爲空
  • 測試根據用戶名查詢到的結果是否滿足預期

單元測試需要程序員自己編寫


2. 單元測試的例子(使用斷言)


斷言:assert

如果assert後面的表達式是一個 True ,表示斷言成功,如果爲一個false,斷言失敗,拋出異常

assert 1>2,"真理是2大於1"

在這裏插入圖片描述


3. Django中單元測試的使用


test.py

編寫單元測試代碼
在這裏插入圖片描述
執行:

python manage.py test appname

在這裏插入圖片描述


4. flask中單元測試的使用


單元測試在python中,用到 unittest模塊 (單例模式)

import unittest

class MyTest(unittest.TestCase):

    def setUp(self):
        """
        在測試執行之前執行,用來加載  準備測試參數
        :return:
        """
        self.a = 10
        self.b = 11

    ## 需要寫測試代碼
    ## 測試方法必須的要求     test_   開頭
    def test_demo(self):
        self.assertEquals(self.a,self.b,msg="a 和 b 不相等")
    def test_demo_has_id(self):
        pass

    def tearDown(self):
        """
        在測試之後執行,用於回收測試環境
        :return:
        """
        pass


if __name__ == '__main__':
    unittest.main()  ## 啓動測試
import unittest
## 導入 create這個工廠方法
from app import Create
from settings import TestConfig
import json


class TestMyTest(unittest.TestCase):
    def setUp(self):
        self.app = Create(TestConfig)
        self.client = self.app.test_client()   ##  屬於flask 提供的測試方法

    def test_has_id(self):
        # 給 mytest 這個路由  發出get請求  傳遞id參數
        # 拿到返回值
        # 判斷返回值是否滿足我們的預期
        #發送get請求    使用urllib和requests發送請求,必須需要flask服務開啓的
        ## 第二種 使用flask提供的方法進行get,post,put,delete請求,不需要開啓flask服務
        resp = self.client.get("/mytest/?id=1")
        ##  首先應該看一下  返回的狀態碼   200
        self.assertEquals(resp.status_code,200)
        ## 判斷返回值 是否符合預期
        result_data =resp.data   ### 返回的 結果
        result_data = result_data.decode()
        data = json.loads(result_data)  ## 反序列化
        ## 判斷返回值中時候有data
        self.assertIn("data",data)    ## 判斷 data 是否在返回值中包含
        ## 判斷返回值中 code 是否爲 10000
        self.assertEquals(data["code"],10001)

    def test_no_id(self):
        pass

    def tearDown(self):
        pass


if __name__ == '__main__':
    unittest.main()


在這裏插入圖片描述


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章