前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flask 学习-64.current_app的使用与应用上下文(AppContext)

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

作者头像
上海-悠悠
发布2022-09-19 15:43:30
2.4K0
发布2022-09-19 15:43:30
举报
文章被收录于专栏:从零开始学自动化测试

前言

在很多框架里面都提到一个词:上下文(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 的使用

先看一个简单的示例

代码语言:javascript
复制
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对象

代码语言:javascript
复制
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.

代码语言:javascript
复制
    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对象,怎样解决呢?

代码语言:javascript
复制
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,其appcontext()返回一个AppContext对象,而其又实现了enter与_exit分别让AppContext对象,即app入栈和出栈,完成了此操作。

AppContext 类的源码如下

代码语言:javascript
复制
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 呢?我们先看一个简单的项目蓝图结构

代码语言:javascript
复制
D:\demo\xuexi_flask
├── apps/
│   ├── __init__.py
│   ├── auth.py
│   ├── blog.py
│   ├── pay.py
├── app.py

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

代码语言:javascript
复制
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示例

代码语言:javascript
复制
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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-09-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 从零开始学自动化测试 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 上下文(Context)
    • 请求上下文(request context)
      • 应用上下文(application context)
      • current_app 的使用
      • AppContext(应用上下文)
      • 在请求中使用current_app
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档