flask上下文管理

一、flask的上下文管理

    与threading.local实现方式相同,创建一个字典{线程或协程唯一标识:要存的数据},保证了数据的隔离;

       而django/tornado框架是通过传参的形式实现的。

     (1)请求上下文流程  

  • 代码启动时,先对LocalStack、Local、LocalProxy三个类初始化
  • 请求进来时:将请求的相关数据封装到了RequestContext中,通过LocalStack将对象添加到Local类中(每个线程协程独立空间存储),     
_request_ctx_stack.local = {
                    唯一标识:{
                        "stack":[ctx, ]
                    },
                }

# ctx.app # 当前APP的名称
# ctx.request # Request对象(封装请求相关东西)
# ctx.session # 空

  • 使用request:  如,print(request)会调用LocalProxy中的__str__方法,request.method会调用LocalProxy中的__getattr方法等,调用这些

          方法就相当于执行了偏函数_lookup_req_object,通过LocalStack去Local中获取值(ctx)

  • 请求终止:通过LocalStack中的pop方法将Local类中属于该请求的数据删除掉

  (2)应用上下文流程(0.9版本之后才有的):就是为了将一个请求周期需要传递的值与request分开,

  其与请求上下文流程相同,就是创建了LocalProxy类的新对象g=LocalProxy(partial(_lookup_app_object, 'g'))

  •  代码启动时,需要对类LocalStack、Local、LocalProxy初始化
  •   当请求到来时,也会将要传递的值封装到RequestContext中,通过LocalStack将对象添加到Local类中(每个线程协程独立空间存储),
_app_ctx_stack.local = {
                    唯一标识:{
                        "stack":[app_ctx, ]
                    },
                    唯一标识:{
                        "stack":[app_ctx, ]
                    },
                }

#app_ctx=AppContext对象
#app_ctx.app:该app对象
#app_ctx.g:每个请求周期都会创建一个用于在请求周期中传递值的一个容器
  • 使用,也会调用LocalProxy中对应方法,从而执行偏函数_lookup_app_object,通过LocalStack类从Local类中获取值(app_ctx)
  • 请求终止时,通过LocalStack中的pop方法将Local类中属于该线程或协程的数据清除掉

二、flask上下文管理相关问题

  • 多线程或协程如何实现
_request_ctx_stack.local = {
                    唯一标识1:{
                        "stack":[ctx, ]
                    },
                    唯一标识2:{
                        "stack":[ctx, ]
                    },
                 ......多少个线程或协程,就有多少个唯一标识
                }

_app_ctx_stack.local = {
                    唯一标识1:{
                        "stack":[app_ctx, ]
                    },
                    唯一标识2:{
                        "stack":[app_ctx, ]
                    },
                ......多少个线程或协程,就有多少个唯一标识                              
                }    
  • flask的local保存数据时,使用了列表创建出栈,为什么使用了栈

          (1)写web程序,web运行环境;栈中永远保存一条数据,可以不用栈

          (2)写脚本获取app信息时,可能存在app上下文嵌套关系,栈中会有多条数据,往出取的时候也是取的自己的数据(测试会用)

from flask import Flask,request,current_app,_app_ctx_stack

app1=Flask('app01')
app1.debug=False


app2=Flask('app02')
app2.debug=True


with app1.app_context():  #__enter__方法-->push-->app_ctx添加到_app_ctx_stack.local
    print(_app_ctx_stack._local.__storage__)
    print(current_app.config['DEBUG'])

    with app2.app_context():  # __enter__方法-->push-->app_ctx添加到_app_ctx_stack.local
        print(_app_ctx_stack._local.__storage__)
        print(current_app.config['DEBUG'])

           (3)Web访问多app应用时,上下文管理是如何实现?

    •   多app应用:在找到路由之前分发好路由,是通过字符串分隔实现,而蓝图是找到路由之后再分发路由
from werkzeug.wsgi import DispatcherMiddleware
from werkzeug.serving import run_simple
from flask import Flask

app1=Flask('app01')
app2=Flask('app02')

@app1.route('/index')
def index():
    return 'index'

@app2.route('/index2')
def index2():
    return 'index2'

# http://localhost:5000/index
# http://localhost:5000/sec/index2
dm=DispatcherMiddleware(app1,{
    '/sec':app2,
})

if __name__ == "__main__":
app2.__call__
run_simple('localhost', 5000, dm)
    • 上下文管理是如何实现

      run_simple('localhost', 5000, dm) 会执行DispatcherMiddleware类的__call__方法

    def __call__(self, environ, start_response):
        script = environ.get('PATH_INFO', '')
        path_info = ''
        while '/' in script:
            if script in self.mounts:
                app = self.mounts[script]
                break
            script, last_item = script.rsplit('/', 1)
            path_info = '/%s%s' % (last_item, path_info)
        else:
            app = self.mounts.get(script, self.app)
        original_script_name = environ.get('SCRIPT_NAME', '')
        environ['SCRIPT_NAME'] = original_script_name + script
        environ['PATH_INFO'] = path_info
        return app(environ, start_response)

返回app(environ,start_response),又调用Flask类的__call__方法

原文地址:https://www.cnblogs.com/meng0410/p/8659276.html