• Flask 学习64.current_app的使用与应用上下文(AppContext) 上海


    前言

    在很多框架里面都提到一个词:上下文(Context),比如django里面的request 就是一个请求上下文对象。
    flask 里面 current_app 用于获取应用app对象。

    上下文(Context)

    什么是上下文(Context)
    维持一段程序正常运行的所需要的外部变量的值的集合,叫做上下文(context)。

    详细描述:
    每一段程序都有很多外部变量。只有像Add这种简单的函数才是没有外部变量的。
    一旦你的一段程序有了外部变量,这段程序就不能独立完整的运行。
    你为了使他们运行,就要给所有的外部变量一个一个写一些值进去。
    这些值的集合就叫上下文。

    Flask中有两种上下文,请求上下文和应用上下文。

    请求上下文(request context)

    request和session都属于请求上下文对象。
    request:封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user'),获取的是get请求的参数。
    session:用来记录请求会话中的信息,针对的是用户信息。举例:session['name'] = user.id,可以记录用户信息。还可以通过session.get('name')获取用户信息。

    应用上下文(application context)

    current_app和g都属于应用上下文对象。
    current_app:表示当前运行程序文件的程序实例。

    current_app 的使用

    先看一个简单的示例

    from flask import Flask, current_app
    
    app = Flask(__name__)
    print(f'app object name: {app}, object id:{id(app)}')
    
    
    if __name__ == '__main__':
        app.run()
    

    使用current_app 获取当前app对象

    from flask import Flask, current_app
    
    
    app = Flask(__name__)
    print(f'app object name: {app}, object id:{id(app)}')
    
    print(f'current app: {current_app}, object id: {id(current_app)}')
    
    if __name__ == '__main__':
        app.run()
    
    

    这时候会出错一个报错:RuntimeError: Working outside of application context.

        raise RuntimeError(_app_ctx_err_msg)
    RuntimeError: Working outside of application context.
    
    This typically means that you attempted to use functionality that needed
    to interface with the current application object in some way. To solve
    this, set up an application context with app.app_context().  See the
    documentation for more information.
    

    意思是说我们在应用上下文之外运行的, current_app 对象并不支持应用之外执行。

    AppContext(应用上下文)

    在flask内部维护者两个线程隔离的栈,current_app指向了AppContext(应用上下文)中的栈顶,request指向了RequestContext(请求上下文)栈顶

    原理图如下

    当请求进入的时候,Request对象被压入栈,从而request有了指向处理请求,接下来会判断AppContext栈顶是否为空,若为空则向栈中压入一个AppContext对象,即app,
    从而current_app就有了指向,所以我们在项目请求中使用是没有报错的,而我们上面的代码不是在请求中实现的所以AppContext栈顶为空
    current_app并没有指向一个AppContext对象,怎样解决呢?

    from flask import Flask, current_app
    
    
    app = Flask(__name__)
    print(app)  # 输出结果:<Flask 'app'>
    with app.app_context():
        app2 = current_app
        print(app2)  # 输出结果:<Flask 'app'>
    
    
    if __name__ == '__main__':
        app.run()
    

    这里我们使用了with,其app_context()返回一个AppContext对象,而其又实现了__enter__与__exit__分别让AppContext对象,即app入栈和出栈,完成了此操作。

    AppContext 类的源码如下

    class AppContext:
        """The application context binds an application object implicitly
        to the current thread or greenlet, similar to how the
        :class:`RequestContext` binds request information.  The application
        context is also implicitly created if a request context is created
        but the application is not on top of the individual application
        context.
        """
    
        def __init__(self, app: "Flask") -> None:
            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
    
        def push(self) -> None:
            """Binds the app context to the current context."""
            self._refcnt += 1
            _app_ctx_stack.push(self)
            appcontext_pushed.send(self.app)
    
        def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None:  # type: ignore
            """Pops the app context."""
            try:
                self._refcnt -= 1
                if self._refcnt <= 0:
                    if exc is _sentinel:
                        exc = sys.exc_info()[1]
                    self.app.do_teardown_appcontext(exc)
            finally:
                rv = _app_ctx_stack.pop()
            assert rv is self, f"Popped wrong app context.  ({rv!r} instead of {self!r})"
            appcontext_popped.send(self.app)
    
        def __enter__(self) -> "AppContext":
            self.push()
            return self
    
        def __exit__(
            self, exc_type: type, exc_value: BaseException, tb: TracebackType
        ) -> None:
            self.pop(exc_value)
    

    在请求中使用current_app

    app对象能直接使用,为什么我们还需要用到current_app 呢?我们先看一个简单的项目蓝图结构

    D:\demo\xuexi_flask
    ├── apps/
    │   ├── __init__.py
    │   ├── auth.py
    │   ├── blog.py
    │   ├── pay.py
    ├── app.py
    

    apps/__init__.py里面我们会写一个create_app() 工厂函数, 并且会导入auth和blog模块注册蓝图

    def create_app():
        app = Flask(__name__)
        # 注册蓝图
        from .auth import api as ns1
        from .blog import api as ns2
        api.add_namespace(ns1)
        api.add_namespace(ns2)
        api.init_app(app)
        
        return app
    

    当auth模块写视图函数,需要获取app对象的config配置对象时,那么又会导入apps/__init__.py,这样就会导致循环导入,所以就有了一个非常方便的获取当前app的对象current_app。
    在请求中使用current_app示例

    from flask import current_app
    
    @app.route('/demo')
    def demo():
        print(current_app.name)
        return {'msg': 'ok'}
    

    其它参考教程https://blog.csdn.net/m0_37323771/article/details/80645100

  • 相关阅读:
    [luogu3393]逃离僵尸岛
    [BZOJ2818]GCD
    [SCOI2015]情报传递
    [NOIP2010]引水入城
    [luogu4315]月下“毛景树”
    「LibreOJ NOI Round #2」不等关系
    [HNOI2013]游走
    Yet Another Minimization Problem
    ZJOI2015 地震后的幻想乡
    [九省联考2018]一双木棋chess
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/16671211.html
Copyright © 2020-2023  润新知