Flask 学习-64.current_app的使用与应用上下文(AppContext)
Posted 上海-悠悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
以上是关于Flask 学习-64.current_app的使用与应用上下文(AppContext)的主要内容,如果未能解决你的问题,请参考以下文章