flask请求上下文(flask向html传递参数)

网友投稿 318 2022-09-04


flask请求上下文(flask向html传递参数)

先看一个例子:

#!/usr/bin/env python# -*- coding:utf-8 -*-import threading# local_values = threading.local()class Foo(object): def __init__(self): self.name = 0local_values = Foo()def func(num): local_values.name = num import time time.sleep(1) print(local_values.name,threading.current_thread().name)for i in range(5): th = threading.Thread(target=func, args=(i,), name='线程%s' % i) th.start()

4 线程04 线程14 线程34 线程24 线程4

上述结果不是我们想要的,local_values.name的值被最后一个覆盖了.............................

flask的request和session设置方式比较新颖,如果没有这种方式,那么就只能通过参数的传递。

flask是如何做的呢?

1. 本地线程,保证即使是多个线程,自己的值也是互相隔离。

#!/usr/bin/env python# -*- coding:utf-8 -*-import threading local_values = threading.local() def func(num): local_values.name = num import time time.sleep(1) print(local_values.name, threading.current_thread().name) for i in range(20): th = threading.Thread(target=func, args=(i,), name='线程%s' % i) th.start()

from _thread import get_identimport threadingdef task(num): print(get_ident())for i in range(10): th = threading.Thread(target=task,args=(i,),name='线程%s' % i) th.start()

View Code

如上图所示,每个线程都有一个唯一标识

__setattr__、__getattr__的用法

class Foo(object): def __init__(self): pass def __setattr__(self, key, value): print(key,value) def __getattr__(self, item): print(item)obj = Foo()obj.xxx=123obj.xxx

__setattr__ __getattr__

自定义Local

1 import threading 2 try: 3 from greenlet import getcurrent as get_ident # 协程 4 except ImportError: 5 try: 6 from thread import get_ident 7 except ImportError: 8 from _thread import get_ident # 线程 9 10 11 class Local(object):12 13 def __init__(self):14 #递归执行__setattr__15 # self.__storage__={}16 # self.__ident_func__=get_ident17 object.__setattr__(self, '__storage__', {})18 object.__setattr__(self, '__ident_func__', get_ident)19 20 21 def __getattr__(self, name):22 try:23 return self.__storage__[self.__ident_func__()][name]24 except KeyError:25 raise AttributeError(name)26 27 def __setattr__(self, name, value):28 ident = self.__ident_func__()29 storage = self.__storage__30 try:31 storage[ident][name] = value32 except KeyError:33 storage[ident] = {name: value}34 35 def __delattr__(self, name):36 try:37 del self.__storage__[self.__ident_func__()][name]38 except KeyError:39 raise AttributeError(name)40 41 42 local_values = Local()43 44 45 def task(num):46 local_values.name = num47 import time48 time.sleep(1)49 print(local_values.name, threading.current_thread().name)50 51 52 for i in range(5):53 th = threading.Thread(target=task, args=(i,),name='线程%s' % i)54 th.start()55 56 57 58 执行结果:59 0 线程060 1 线程161 2 线程262 4 线程463 3 线程3

View Code

2. 上下文原理

#!/usr/bin/env python# -*- coding:utf-8 -*-from functools import partialfrom flask.globals import LocalStack, LocalProxy ls = LocalStack() class RequestContext(object): def __init__(self, environ): self.request = environ def _lookup_req_object(name): top = ls.top if top is None: raise RuntimeError(ls) return getattr(top, name) session = LocalProxy(partial(_lookup_req_object, 'request')) ls.push(RequestContext('c1')) # 当请求进来时,放入print(session) # 视图函数使用print(session) # 视图函数使用ls.pop() # 请求结束pop ls.push(RequestContext('c2'))print(session) ls.push(RequestContext('c3'))print(session)

View Code

3. Flask内部实现

#!/usr/bin/env python# -*- coding:utf-8 -*- from greenlet import getcurrent as get_ident def release_local(local): local.__release_local__() class Local(object): __slots__ = ('__storage__', '__ident_func__') def __init__(self): # self.__storage__ = {} # self.__ident_func__ = get_ident object.__setattr__(self, '__storage__', {}) object.__setattr__(self, '__ident_func__', get_ident) def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() storage = self.__storage__ try: storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) class LocalStack(object): def __init__(self): self._local = Local() def __release_local__(self): self._local.__release_local__() def push(self, obj): """Pushes a new item to the stack""" rv = getattr(self._local, 'stack', None) if rv is None: self._local.stack = rv = [] rv.append(obj) return rv def pop(self): """Removes the topmost item from the stack, will return the old value or `None` if the stack was already empty. """ stack = getattr(self._local, 'stack', None) if stack is None: return None elif len(stack) == 1: release_local(self._local) return stack[-1] else: return stack.pop() @property def top(self): """The topmost item on the stack. If the stack is empty, `None` is returned. """ try: return self._local.stack[-1] except (AttributeError, IndexError): return None stc = LocalStack() stc.push(123)v = stc.pop() print(v)

View Code

------------------------------------------源码分析(重点):----------------------------------------------------------------------

首先看这段代码:

if __name__ == '__main__': # 1.1 app.__call__ app.request_class response_class = Response app.run()

在上面提到了上下文的原理:

-----------------------LocalStak-----------------Local-----------------------------------

globals.py中有这么段代码,定义了request

# \flask\globals.py# context locals# 全局变量_request_ctx_stack = LocalStack()# _app_ctx_stack = LocalStack()# current_app = LocalProxy(_find_app)request = LocalProxy(partial(_lookup_req_object, 'request'))session = LocalProxy(partial(_lookup_req_object, 'session'))# g = LocalProxy(partial(_lookup_app_object, 'g'))

# Local类

class Local(object): __slots__ = ('__storage__', '__ident_func__') def __init__(self): object.__setattr__(self, '__storage__', {}) object.__setattr__(self, '__ident_func__', get_ident) def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() storage = self.__storage__ """ { 唯一标识(__ident_func__):{ stack:[ctx/requestcontext,]//栈 } } """ try: storage[ident][name] = value except KeyError: storage[ident] = {name: value}

# LocalStack类

def __init__(self): self._local = Local()# LocalStack()就是我们之前写过的一个多线程数据相互隔离的类,每个线程都有一个唯一标识# 每个线程都有一个独立的存储数据和访问数据的空间# __storage__ __ident_func__# \werkzeug\local.py# Local类 每个线程唯一标识 可以存储数据# LocalStack类 实例化Local类# LocalStack类def push(self, obj): # 刚开始stack=[] rv = getattr(self._local, 'stack', None) if rv is None: self._local.stack = rv = [] rv.append(obj)//执行Local的__setattr__方法 return rvdef pop(self): stack = getattr(self._local, 'stack', None) if stack is None: return None elif len(stack) == 1: release_local(self._local) return stack[-1] else: return stack.pop()

以上都是启动会加载的文件 ,创建唯一标识,并给这个标识赋值 stack=[],stack是个栈,存储着每个用户请求的对象(数据)RequestContext对象(ctx)

{唯一标识(__ident_func__):{stack:[ctx/requestcontext,]//栈}}

--------------------------------接下来开始看看执行流程:---------(app)-----------------------------------------------

app.__call__

app.request_class

response_class = Response

app.run()

执行app.__call__方法

app.run()    =》run_simple(host, port, self, **options) =》app.__call__

# \flask\app.py

# 主要从这里入手 分析代码

# Flask类(app)

app.py

def __call__(self, environ, start_response): return self.wsgi_app(environ, start_response)

app.py

def wsgi_app(self, environ, start_response): #将请求相关的数据environ封装到了RequestContext对象中 #再将对象封装到Local中(每个线程/每个协程独立空间存储) #request_context对象中有如下: #ctx.app 当前APP #ctx.request Request对象(封装请求相关的东西) #ctx.session #RequestContext类 ctx.py ctx = self.request_context(environ) error = None try: try: #RequestContext.push()->LocalStack.push #将封装了请求相关的RequestContext对象中的对象添加到LocalStack.push(self) #_request_ctx_stack = LocalStack() """ { 唯一标识(__ident_func__):{ stack:[ctx/requestcontext,]//栈 } } """ # 将ctx通过LocalStack添加到Local中 # 此时ctx中的session中已经有值了 ctx.push() response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None #RequestContext.pop()->LocalStack.pop(_request_ctx_stack.pop()) #_request_ctx_stack = LocalStack() #最后将自己请求在Local中保存的数据清除 ctx.auto_pop(error)

ctx = self.request_context(environ) 记住了!!!

def request_context(self, environ):  #self=app return RequestContext(self, environ)

ctx就是request_context对象#request_context对象中有如下属性:  #ctx.app 当前APP  #ctx.request Request对象(封装请求相关的东西)  #ctx.session

ctx.push():

#执行RequestContext.push()->LocalStack.push #将封装了请求相关的RequestContext对象中的对象添加到LocalStack.push(self) """ { 唯一标识(__ident_func__):{ stack:[ctx/requestcontext,]//栈 } } """

--------------------------RequestContext------------ctx.py-------------------------------------

# \flask\ctx.py# RequestContext类def __init__(self, app, environ, request=None): self.app = app if request is None: request = app.request_class(environ) self.request = request self.url_adapter = app.create_url_adapter(self.request) self.flashes = None self.session = None self._implicit_app_ctx_stack = [] self.preserved = False self._preserved_exc = None self._after_request_functions = [] self.match_request()request = app.request_class(environ)#ctx.pushdef push(self): top = _request_ctx_stack.top if top is not None and top.preserved: top.pop(top._preserved_exc) app_ctx = _app_ctx_stack.top if app_ctx is None or app_ctx.app != self.app: # 应用上下文 # app_ctx = AppContext对象 # app_ctx.app = Flask对象 # app_ctx.g=对象(用于一个周期内要保存的值,相当于全局变量) app_ctx = self.app.app_context() app_ctx.push() self._implicit_app_ctx_stack.append(app_ctx) else: self._implicit_app_ctx_stack.append(None) if hasattr(sys, 'exc_clear'): sys.exc_clear() #selft 是RequestContext对象 也是app里面的ctx,包含了请求所有数据 #_request_ctx_stack = LocalStack() #相当于执行LocalStack.push() #self 就是RequestContext类 _request_ctx_stack.push() if self.session is None: session_interface = self.app.session_interface self.session = session_interface.open_session( self.app, self.request ) if self.session is None: self.session = session_interface.make_null_session(self.app)

RequestContext对象中有request,session等对象,#ctx.push执行的就是RequestContext.push,其中有这么段代码

_request_ctx_stack.push()

#self 是RequestContext对象 也是app里面的ctx,包含了请求所有数据 #_request_ctx_stack = LocalStack() #相当于执行LocalStack.push() #self 就是RequestContext类   _request_ctx_stack.push()

组中执行的是Local中​​ ​​​​storage[ident][name] ​​​​=​​​​value​​

最终目的就是把RequestContext对象也就是ctx放到localStack中的唯一标识字典中的stack栈中

以上明白了吗?不明白可以去看看源码。。。。。  现在已经出现了ctx=RequestContext     app    LocalStack      Local     四个类globals.py  刚开始启动加载的文件

到这里 请求上下文已经差不多了,每个请求都有一个独立数据存储空间,互补影响彼此。就是把request对象放到RequestContext.request属性里面,然后又把RequestContext对象放到每个线程(请求)或者是协程的一个唯一标识的stack栈中

"""

storage:{

唯一标识(__ident_func__):{

stack:[ctx/requestcontext,]//栈

}

}

"""

视图中执行print(request)发生了什么?

--------------------------request-------------------------------------------------

# \flask\globals.py# 视图中执行print(request)发生了什么?# 执行LocalProxy类中的__str__方法# 在globals.py文件中 (最开始在加载的文件)# context locals_request_ctx_stack = LocalStack()_app_ctx_stack = LocalStack()current_app = LocalProxy(_find_app)request = LocalProxy(partial(_lookup_req_object, 'request'))session = LocalProxy(partial(_lookup_req_object, 'session'))g = LocalProxy(partial(_lookup_app_object, 'g'))# 偏函数做了啥?#partial(_lookup_req_object, 'request')# 函数_lookup_req_object 去LocalStack获取唯一标识栈里面的值(RequestContext对象)def _lookup_req_object (name): # _request_ctx_stack = LocalStack() # 返回的其实就是刚开始塞到每个线程唯一标识里面的那个ctx/requestContext对象 top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) # 到requestContext对象取request属性值 request对象 return getattr(top, name)# LocalProxy类中的__str__方法 最终就是执行_lookup_req_object函数而已__setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)在LocalStack类中: @property def top(self): """The topmost item on the stack. If the stack is empty, `None` is returned. """ try: return self._local.stack[-1] except (AttributeError, IndexError): return None def _get_current_object(self): if not hasattr(self.__local, '__release_local__'): # 执行_lookup_req_object函数 return self.__local() try: return getattr(self.__local, self.__name__) except AttributeError: raise RuntimeError('no object bound to %s' % self.__name__)

request = LocalProxy(partial(_lookup_req_object, 'request'))这段代码要仔细看看了

print(request)  =>       LocalProxy。__str__    =>       LocalProxy.__str__ = lambda x: str(x._get_current_object()) =>( _get_current_object 里面执行了 )self.__local()

LocalProxy.__local  就是传入的偏函数(

partial(_lookup_req_object, 'request')

最后执行self.__local() 相当于执行偏函数,返回request对象了

LocalProxy类中

def __init__(self, local, name=None): #Local = partial(_lookup_req_object, 'request') #_local = _LocalProxy__local私有字段访问 object.__setattr__(self, '_LocalProxy__local', local) object.__setattr__(self, '__name__', name) if callable(local) and not hasattr(local, '__release_local__'): object.__setattr__(self, '__wrapped__', local) def _get_current_object(self): if not hasattr(self.__local, '__release_local__'): return self.__local() try: return getattr(self.__local, self.__name__) except AttributeError:

_request_ctx_stack的应用

# _request_ctx_stack的应用from flask.globals import _request_ctx_stackfrom functools import partialdef _lookup_req_object(name): # name = request # top= ctx top = _request_ctx_stack.top if top is None: raise RuntimeError('不存在') # return ctx.request return getattr(top, name)class Foo(object): def __init__(self): self.xxx = 123 self.ooo = 888req = partial(_lookup_req_object,'xxx')xxx = partial(_lookup_req_object,'ooo')# 当前求刚进来时_request_ctx_stack.push(Foo())# 使用# obj = _request_ctx_stack.top# obj.xxxv1 = req()print(v1)v2 = xxx()print(v2)# 请求终止,将local中的值移除_request_ctx_stack.pop()

_request_ctx_stack

上下文 - threading.Local和Flask自定义Local对象- 请求到来 - ctx = 封装RequestContext(request,session) - ctx放到Local中- 执行视图时 - 导入request - print(request) --> LocalProxy对象的__str__ - request.method --> LocalProxy对象的__getattr__ - request + 1 --> LocalProxy对象的__add__ - 调用 _lookup_req_object函数:去local中将requestContext想获取到,再去requestContext中获取request或session- 请求结束 - ctx.auto_pop() - ctx从local中移除。

多app应用

from werkzeug.wsgi import DispatcherMiddlewarefrom werkzeug.serving import run_simplefrom flask import Flask, current_appapp1 = Flask('app01')app2 = Flask('app02')@app1.route('/index')def index(): return "app01"@app2.route('/index2')def index2(): return "app2"# = DispatcherMiddleware(app1, { '/sec': app2,})if __name__ == "__main__": app2.__call__ run_simple('localhost', 5000, dm)

View Code

离线脚本:

from flask import Flask,current_app,globals,_app_ctx_stackapp1 = Flask('app01')app1.debug = False # 用户/密码/邮箱# app_ctx = AppContext(self):# app_ctx.app# app_ctx.gapp2 = Flask('app02')app2.debug = True # 用户/密码/邮箱# app_ctx = AppContext(self):# app_ctx.app# app_ctx.gwith app1.app_context():# __enter__方法 -> push -> app_ctx添加到_app_ctx_stack.local # {: {'stack': []}} print(_app_ctx_stack._local.__storage__) print(current_app.config['DEBUG']) with app2.app_context(): # {: {'stack': [ ]}} print(_app_ctx_stack._local.__storage__) print(current_app.config['DEBUG']) print(current_app.config['DEBUG'])

View Code

"""需求:不用数据库连接池,显示数据库连接"""class SQLHelper(object): def open(self): pass def fetch(self,sql): pass def close(self): pass def __enter__(self): self.open() return self def __exit__(self, exc_type, exc_val, exc_tb): self.close()# obj = SQLHelper()# obj.open()# obj.fetch('select ....')# obj.close()with SQLHelper() as obj: # 自动调用类中的__enter__方法, obj就是__enter__返回值 obj.fetch('xxxx') # 当执行完毕后,自动调用类 __exit__ 方法# 以后如果遇到:

View Code


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:详解Java中方法重写和方法重载的6个区别
下一篇:sorted排序(sorted排序遇到相同数值)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~