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


在这里插入图片描述


发布了107 篇原创文章 · 获赞 43 · 访问量 6158

猜你喜欢

转载自blog.csdn.net/langdei/article/details/102614742