Flask上下文管理源碼分析 ——(3)

引出的問題

Flask如何使用上下文臨時把某些對象變爲全局可訪問python

首先咱們作以下的幾種狀況的假設

狀況一:單進程單線程web

這種狀況能夠基於全局變量存儲臨時的對象sql

狀況二:單進程多線程shell

這種狀況會出現多個線程共享全局的變量,爲了每一個線程中的數據不被其餘線程修改,能夠藉助hreading.local對象,爲每一個線程作惟一的表示用來作鍵,請求的對象做爲值來實現flask

多線程共享數據的問題session

import threading
class Foo(object):
    def __init__(self):
        self.name = 0

local_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(20):
    th = threading.Thread(target=func, args=(i,), name='線程%s' % i)
    th.start()

 

咱們能夠看到最後把每一個線程中對象中name值都變爲了19,不能保證每一個線程中對象中的值惟一多線程

使用hreading.local對象能夠對每一個線程作惟一的表示能夠解決上述的問題app

 

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

 

能夠看到每一個線程中的值惟一ide

- 狀況三:單進程單線程(多個協程)Flask 的上下文管理就是基於這種狀況作的函數

 在這種狀況下使用上面的方法能夠保證線程中的數據惟一,可是使用其內部建立多個協程後,hreading.local只能對線程做惟一的標示,協程是在單線程下切換的,因此多個協程還會出現共享數據的問題

解決的思路:爲每一個程作惟一的標示,咱們能夠經過python自帶的greenlet模塊中的getcurrent來實現

只需對上面的代碼作簡單的修改便可

import threading
try:
    from greenlet import getcurrent as get_ident # 協程
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident # 線程


class Local(object):
    def __init__(self):
        self.storage = {}
        self.get_ident = get_ident

    def set(self,k,v):
        ident = self.get_ident()
        origin = self.storage.get(ident)
        if not origin:
            origin = {k:v}
        else:
            origin[k] = v
        self.storage[ident] = origin

    def get(self,k):
        ident = self.get_ident()
        origin = self.storage.get(ident)
        if not origin:
            return None
        return origin.get(k,None)

local_values = Local()


def task(num):
    local_values.set('name',num)
    import time
    time.sleep(1)
    print(local_values.get('name'), threading.current_thread().name)


for i in range(20):
    th = threading.Thread(target=task, args=(i,),name='線程%s' % i)
    th.start()

測試的結果以下

使用面向對象中方法對其進行簡單的優化

在初始化的時候設置屬性的時候,爲了不循環引用,咱們能夠這樣作  object.__setattr__(self, 'storage', {})

class Foo(object):

    def __init__(self):
        object.__setattr__(self, 'storage', {})
        # self.storage = {}

    def __setattr__(self, key, value):
        self.storage = {'k1':'v1'}
        print(key,value)

    def __getattr__(self, item):
        print(item)
        return 'df'


obj = Foo()

# obj.x = 123
# 對象.xx

修改後的代碼以下所示 

import threading
try:
    from greenlet import getcurrent as get_ident # 協程
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident # 線程


class Local(object):

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


local_values = Local()


def task(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=task, args=(i,),name='線程%s' % i)
    th.start()

偏函數 (幫助咱們傳遞參數)

import functools

def func(a1):
    print(a1)


new_func = functools.partial(func,666)

new_func() 

運行結果以下

面向對象中的魔法方法的簡單使用

import flask.globals
class Foo(object):

    def __init__(self,num):
        self.num = num

    def __add__(self, other):
        data = self.num + other.num
        return Foo(data)

obj1 = Foo(11)
obj2 = Foo(22)

v = obj1 + obj2
print(v.num)  

 

運行結果以下

chain 幫助咱們拼接列表中的值

from itertools import chain

# def f1(x):
#     return x + 1
#
# func1_list = [f1,lambda x:x-1]
#
# def f2(x):
#     return x + 10
#
#
# new_fun_list = chain([f2],func1_list)
# for func in new_fun_list:
#     print(func)


v1 = [11,22,33]
v2 = [44,55,66]

new = chain(v1,v2)
for item in new:
    print(item)  

測試結果以下

Flask上下文源碼分析

Flask中有兩種上下文,請求上下文和應用上下文。

Flask上下文大體能夠分爲3個步奏

  1 請求到來的時候(爲每一個線程/協程開闢獨立的空間,存入statck中)   

    - ctx = 封裝RequestContext(request,session) 

      - ctx放到Local中

  2 執行視圖函數的時候(調用每一個線程本身的數據)

    - 導入request
    - 調用 _lookup_req_object函數:去local中將requestContext想獲取到,再去requestContext中獲取request或session

  3- 請求結束(把數據從stack中刪除)

    - ctx.auto_pop()    

    - ctx從local中移除。

當程序啓動時執行run方法中的 run_simple方法

請求上下文  (request,session)

封裝請求相關的數據

    def run(self, host=None, port=None, debug=None,
            load_dotenv=True, **options):
        
        _host = '127.0.0.1'
        _port = 5000
        server_name = self.config.get('SERVER_NAME')
        sn_host, sn_port = None, None

        if server_name:
            sn_host, _, sn_port = server_name.partition(':')

        host = host or sn_host or _host
        port = int(port or sn_port or _port)

        options.setdefault('use_reloader', self.debug)
        options.setdefault('use_debugger', self.debug)
        options.setdefault('threaded', True)

        cli.show_server_banner(self.env, self.debug, self.name, False)

        from werkzeug.serving import run_simple

        try:
            run_simple(host, port, self, **options)
        finally:
            # reset the first request information if the development server
            # reset normally.  This makes it possible to restart the server
            # without reloader and that stuff from an interactive shell.
            self._got_first_request = False

 

當請求來的時候會執行 run_simple中的self對象,也就是app.__call__方法,代碼以下

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

查看源碼中的wsgi_app方法,參數environ表示全部請求的數據,start_response表示響應

    def wsgi_app(self, environ, start_response):
        # 將請求相關的數據environ 封裝到request_context 對象中
        ctx = self.request_context(environ) # 生成一個類
        error = None
        try:
            try:
                # 把請求的對象封裝到local中,每一個線程 / 協程都是獨立的空間存儲
                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
            # 最後把請求在local中的數據刪掉
            ctx.auto_pop(error)

 

在上面的源碼中咱們能夠看到把全部請求相關的數據封裝到了,self.request_context(environ)中,其返回一個RequestContext賦值給ctx咱們繼續追蹤其內部的代碼以下所示

    def request_context(self, environ):
     # self指的是app對象
        return RequestContext(self, environ)

 

咱們能夠看到RequestContext類對environ進行了封裝,在這裏咱們能夠看到session的數據爲None其初始化的方法以下:

class RequestContext(object):
    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._after_request_functions = []
        self.match_request()

 

把請求相關的數據添加到Local對象的storage中

 咱們繼續追蹤wsgi_app中的ctx.push的代碼以下

    def 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 = 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()
        # self 是request_contenx的對象,其中包含了請求相關的全部數據
        #  _request_ctx_stack==>LocalStack
        _request_ctx_stack.push(self)

        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)

在最下面咱們看到了給session中的數據從新賦了值 

咱們查看_request_ctx_stack類和其內部的push方法,把請求封裝後的數據_request_ctx當作參數傳遞進去

_request_ctx_stack = LocalStack()

  

繼續追蹤LocalStack類中的push方法代碼以下

    def push(self, obj):
        """Pushes a new item to the stack"""
        rv = getattr(self._local, 'stack', None)
        if rv is None:
            # 執行local 對象的__setatr__方法
            self._local.stack = rv = []
        # 把requestContext 對象添加到列表中 self._local.stack = rv = [把requestContext]
        rv.append(obj)
        return rv

 

在上面的源碼中有一個賦值的操做self._local.stack=rv=[],self.local=local()會觸發local()對象中的__setatr__方法參數key=stack,value=[],其__setatr__方法代碼以下所示

    def __setattr__(self, name, value):
        # name = stack value = []
        # {"惟一的表示":
        # {stack:[requestContext(ctx)]}
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

 

ident = self.__ident_func__() 表示的是爲線程/協程作惟一的標示,也就覺得者當前的請求的上下文添加到了這樣的一個字典中

 

繼續追蹤LocalStack類中的push方法中的下rv.append(obj),把當前請求相關的數據添加到stoage中,obj是請求相關的數據RequestCotent, 

{
    "線程/協助的惟一表示" : {"stack":["當前請求相關的數據"]}
    
}

  

當請求結束的時候刪除storage中的,當前請求的數據  

咱們回去繼續追蹤wsgi_app中 ctx.auto_pop(error)方法刪除請求結束後的數據

request和session使用內部調用源碼分析

from flask import Flask,request  

其代碼以下

from functools import partial
from werkzeug.local import LocalStack, LocalProxy

def _lookup_req_object(name):
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    # 去requestContext中獲取request的值
    return getattr(top, name)


def _lookup_app_object(name):
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    return getattr(top, name)


def _find_app():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    return top.app


# context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
# partial 偏函數
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))

 

 在上面的源碼中,咱們能夠看到request是一個LocalProxy對象,其內部的參數經過偏函數partial調用_lookup_req_object函數傳的參數默認爲'request",_lookup_req_object代碼以下

def _lookup_req_object(name):
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    # 去requestContext中獲取request的值
    return getattr(top, name)

 

top調用的是_app_ctx_stack= LocalStack()類中的top方法,代碼以下

    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

其返回的是存儲在storage{"stack":{"當前請求":[RequestContent(當前請求的數據)]}},中當前請求的數據,也就是RequestContent對象,因此上面的_llokup_req_object.函數返回的是RequestContent中的g

class RequestContext(object):

    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

 

因此request = LocalProxy(RequestContent.request)和session= LocalProxy(RequestContent.session)建立一個類,其初始化的方法以下

class LocalProxy(object):

    __slots__ = ('__local', '__dict__', '__name__', '__wrapped__')

    def __init__(self, local, name=None):
        # self.__loacl = local  local 指的是request
        object.__setattr__(self, '_LocalProxy__local', local)
        object.__setattr__(self, '__name__', name)
        if callable(local) and not hasattr(local, '__release_local__'):
            # "local" is a callable that is not an instance of Local or
            # LocalManager: mark it as a wrapped function.
            object.__setattr__(self, '__wrapped__', local)

 

經過上面的賦值咱們能夠知道,最終把RequestContent.reques賦值給self.local = RequestContent.reques

補充知識:面向對象的經過經過私有字段的取值

class Foo(object):

    def __init__(self):
        self.name = 'alex'
        self.__age = 18

    def get_age(self):
        return self.__age

obj = Foo()
# 強制獲取私有字段
print(obj._Foo__age)

 

當咱們使用request中的方法的時候,會執行其內部的魔法方法如:

- print(request)   -->  LocalProxy對象的__str__
- request.method   -->  LocalProxy對象的__getattr__
- request + 1      -->  LocalProxy對象的__add__

經過以上Flask源碼的解讀,咱們能夠試着傳遞一些值作一些簡單的修改

from flask.globals import _request_ctx_stack
from functools import partial

def _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 = 888

req = partial(_lookup_req_object,'xxx')
xxx = partial(_lookup_req_object,'ooo')

# 當前求剛進來時
_request_ctx_stack.push(Foo())

# 使用
# obj = _request_ctx_stack.top
# obj.xxx
v1 = req()
print(v1)
v2 = xxx()
print(v2)

# 請求終止,將local中的值移除
_request_ctx_stack.pop()

後臺打印的結果以下

應用上下文(current__app,g)

源碼wsgi_app

    def wsgi_app(self, environ, start_response):
        # 將請求相關的數據environ 封裝到request_context 對象中
        # ctx.app = app
        # ctx.request = app.request_class(environ)
        ctx = self.request_context(environ) # 生成一個類
        error = None
        try:
            try:
                # 把請求的對象封裝到local中,每一個線程 / 協程都是獨立的空間存儲
                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
            # 最後把請求在local中的數據刪掉
            ctx.auto_pop(error)

追蹤ctx.push代碼以下

    def 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(object)  app_ctx.g   app_ctx.app
            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()
        # self 是request_contenx的對象,其中包含了請求相關的全部數據
        #  _request_ctx_stack==>LocalStack
        _request_ctx_stack.push(self)

        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)

app_cxt = self.app.app_context() 的源碼瞭解到其返回的是一個AppContext對象

    def app_context(self):
        return AppContext(self)

AppContext源碼以下

class AppContext(object):
    def __init__(self, app):
        self.app = app
        self.url_adapter = app.create_url_adapter(None)
        self.g = app.app_ctx_globals_class()

        # Like request context, app contexts can be pushed multiple times
        # but there a basic "refcount" is enough to track them.
        self._refcnt = 0

 咱們追蹤g變量的源碼發現其用法相似字典

class _AppCtxGlobals(object):

    def get(self, name, default=None):

        return self.__dict__.get(name, default)

    def pop(self, name, default=_sentinel):

        if default is _sentinel:
            return self.__dict__.pop(name)
        else:
            return self.__dict__.pop(name, default)

    def setdefault(self, name, default=None):

        return self.__dict__.setdefault(name, default)

 

 回到上面的代碼  app_ctx對象,從中咱們能夠拿到app_ctx和app_ctx.app這就是咱們要找的應用上下文了

繼續追蹤 app_ctx.push()源碼以下

    def push(self):
        self._refcnt += 1
        if hasattr(sys, 'exc_clear'):
            sys.exc_clear()
        _app_ctx_stack.push(self)
        appcontext_pushed.send(self.app)

追蹤 _app_ctx_stack = LocalStack()中的push方法

    def push(self, obj):
        """Pushes a new item to the stack"""
        rv = getattr(self._local, 'stack', None)
        if rv is None:
            # 執行local 對象的__setatr__方法
            self._local.stack = rv = []
        # 把requestContext 對象添加到列表中 self._local.stack = rv = [把requestContext]
        rv.append(obj)
        return rv

 

 

在上面的源碼中有一個賦值的操做self._local.stack=rv=[],self.local=local()會觸發local()對象中的__setatr__方法參數key=stack,value=[],其__setatr__方法代碼以下所示

    def __setattr__(self, name, value):
        # name = stack value = []
        # {"惟一的表示":
        # {stack:[requestContext(ctx)]}
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

  

繼續追蹤LocalStack類中的push方法中的下rv.append(obj),把當前請求相關的數據添加到stoage中,obj是請求相關的數據RequestCotent, 

{
    "stack" : {"線程/協助的惟一表示":["當前請求相關的數據"]}
    
}

g變量和 current_app

from flask import Flask,g,current_app  
其代碼以下
from functools import partial
from werkzeug.local import LocalStack, LocalProxy

def _lookup_req_object(name):
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    # 去requestContext中獲取request的值
    return getattr(top, name)


def _lookup_app_object(name):
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    return getattr(top, name)


def _find_app():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    return top.app


# context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
# partial 偏函數
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))

 在上面的源碼中,咱們能夠看到g是一個LocalProxy對象,其內部的參數經過偏函數partial調用_lookup_app_object函數傳的參數默認爲'g",_lookup_app_object代碼以下

def _lookup_req_object(name):
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    # 去requestContext中獲取request的值
    return getattr(top, name)

top調用的是app_cxt =  LocalStack() 類中的top方法,代碼以下

    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

其返回的是存儲在storage{"stack":{"當前請求":[RequestContent(當前請求的數據)]}},中當前請求的數據,也就是RequestContent對象,因此上面的_llokup_req_object.函數返回的是AppContext中的g

class AppContext(object):

    def __init__(self, app):
        self.app = app
        self.url_adapter = app.create_url_adapter(None)
        self.g = app.app_ctx_globals_class()

        # Like request context, app contexts can be pushed multiple times
        # but there a basic "refcount" is enough to track them.
        self._refcnt = 0

 

 

因此g= LocalProxy(AppContext.g) 和   current_app = LocalProxy(AppContext.app)建立一個類,其初始化的方法以下

class LocalProxy(object):

    __slots__ = ('__local', '__dict__', '__name__', '__wrapped__') def __init__(self, local, name=None): # self.__loacl = local local 指的是request object.__setattr__(self, '_LocalProxy__local', local) object.__setattr__(self, '__name__', name) if callable(local) and not hasattr(local, '__release_local__'): # "local" is a callable that is not an instance of Local or # LocalManager: mark it as a wrapped function. object.__setattr__(self, '__wrapped__', local)

 

當咱們使用g和current_app 中的方法的時候,會執行其內部的魔法方法如:

- print(g)   -->  LocalProxy對象的__str__
- g.get(')-->  LocalProxy對象的__getattr__

多app應用

from werkzeug.wsgi import DispatcherMiddleware
from werkzeug.serving import run_simple
from flask import Flask, current_app

app1 = Flask('app01')

app2 = Flask('app02')



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


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

# http://www.oldboyedu.com/index
# http://www.oldboyedu.com/sec/index2
dm = DispatcherMiddleware(app1, {
    '/sec': app2,
})

if __name__ == "__main__":
    app2.__call__
    run_simple('localhost', 5000, dm)

 

 

with在類中的使用

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

with SQLHelper() as obj: # 自動調用類中的__enter__方法, obj就是__enter__返回值
    obj.fetch('xxxx')
    # 當執行完畢後,自動調用類 __exit__ 方法

flask的local中保存數據時,使用列表建立出來的棧。爲何用棧?

在寫腳本的時候一個線程中執行多個app他們的關係仍是嵌套的

        - 若是寫web程序,web運行環境;棧中永遠保存1條數據(能夠不用棧)。

        - 寫腳本獲取app信息時,可能存在app上下文嵌套關係。

from flask import Flask,current_app,globals,_app_ctx_stack

app1 = Flask('app01')
app1.debug = False # 用戶/密碼/郵箱
# app_ctx = AppContext(self):
# app_ctx.app
# app_ctx.g

app2 = Flask('app02')
app2.debug = True # 用戶/密碼/郵箱
# app_ctx = AppContext(self):
# app_ctx.app
# app_ctx.g



with app1.app_context():# __enter__方法 -> push -> app_ctx添加到_app_ctx_stack.local
    # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438>]}}
    print(_app_ctx_stack._local.__storage__)
    print(current_app.config['DEBUG'])

    with app2.app_context():
        # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438> ]}}
        print(_app_ctx_stack._local.__storage__)
        print(current_app.config['DEBUG'])

    print(current_app.config['DEBUG'])

 

打印的數據以下

 關於g變量簡單的使用

 咱們能夠在請求到來的時候,給用戶賦予一些權限,在視圖函數中使用

from flask import Flask,request,g

app = Flask(__name__)

@app.before_request
def before():
    g.permission_code_list = ['list','add']


@app.route('/',methods=['GET',"POST"])
def index():
    print(g.permission_code_list)
    return "index"


if __name__ == '__main__':
    app.run()
相關文章
相關標籤/搜索