当前位置:   article > 正文

Flask框架中上下文(请求上下文和应用上下文)用法详解_with app.app_context():

with app.app_context():

上下文:即语境,语意,在程序中可以理解为在代码执行到某一时刻时,根据之前代码所做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以完成的事情。维持一段程序正常运行的所需要的外部变量的值的集合,叫做上下文(context)

        上下文的一个典型应用场景就是用来缓存一些我们需要在发生请求之前或者要使用的资源。举个例子,比如数据库连接。当我们在应用上下文中来存储东西的时候你得选择一个唯一的名字,这是因为应用上下文为 Flask 应用和扩展所共享。

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

Flask中上下文对象:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。

1、请求上下文(request context)

思考:在视图函数中,如何取到当前请求的相关数据?比如:请求地址,请求方式,cookie等等

        在 flask 中,可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据,请求上下文对象有:request、session

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

请求上下文也是存放到一个 LocalStack 的栈中。

和请求相关的操作就必须用到请求上下文,比如使用 url_for 反转视图函数。

注意

在视图函数中,不用担心请求上下文的问题。

因为视图函数要执行,那么肯定是通过访问url的方式执行的,

那么这种情况下,Flask底层就已经自动的帮我们把应用上下文和请求上下文都推入到了相应的栈中。

注意

如果想要在视图函数外面执行相关的操作,比如反转url,那么就必须要手动推入请求上下文:

底层代码执行说明:

1. 推入请求上下文到栈中,会首先判断有没有应用上下文

2. 如果没有那么就会先推入应用上下文到栈中

3. 然后再推入请求上下文到栈中

为什么上下文需要放在栈中?

1. 应用上下文:Flask底层是基于werkzeug,werkzeug是可以包含多个app的,所以这时候用一个栈来保存。如果你在使用app1,那么app1应该是要在栈的顶部,如果用完了app1,那么app1应该从栈中删除。方便其他代码使用下面的app。

2. 如果在写测试代码,或者离线脚本的时候,我们有时候可能需要创建多个请求上下文,这时候就需要存放到一个栈中了。使用哪个请求上下文的时候,就把对应的请求上下文放到栈的顶部,用完了就要把这个请求上下文从栈中移除掉。

示例代码:

  1. from flask import Flask, url_for
  2. app = Flask(__name__)
  3. @app.route('/')
  4. def index():
  5. url = url_for('request_url')
  6. return f'Hello!==={url}'
  7. @app.route('/test/')
  8. def request_url():
  9. return '这个是为了测试请求上下文'
  10. # RuntimeError: Application was not able to create a URL adapter for request independent URL generation.
  11. # You might be able to fix this by setting the SERVER_NAME config variable.
  12. # with app.app_context():
  13. # url = url_for('request_url')
  14. # print(url)
  15. with app.test_request_context():
  16. url = url_for('request_url')
  17. print(url)
  18. if __name__ == '__main__':
  19. app.run(debug=True)

运行结果:

2、应用上下文(application context)

        它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。

        类型是LocalProxy,像全局变量一样工作,但只能在处理请求期间且在处理它的线程中访问,返回的栈顶元素不是应用上下文,而是flask的应用实例对象。

        应用上下文的封装 = flask核心对象+和外部协作对象(在flask封装对象上再添加push、pop等)(请求上下文同理)

        应用上下文是存放到一个 LocalStack 的栈中。和应用app相关的操作就必须要用到应用上下文。

应用上下文对象有:current_app,g

注意

在视图函数中,不用担心应用上下文的问题。因为视图函数要执行,那么肯定是通过访问url的方式执行的,那么这种情况下,Flask底层就已经自动的帮我们把应用上下文都推入到了相应的栈中。

如果想要在视图函数外面执行相关的操作,比如: 获取当前的app名称,那么就必须要手动推入应用上下文

2.1 current_app

        应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:

  • 应用的启动脚本是哪个文件,启动时指定了哪些参数
  • 加载了哪些配置文件,导入了哪些配置
  • 连了哪个数据库
  • 有哪些public的工具类、常量
  • 应用跑再哪个机器上,IP多少,内存多大

示例代码1:

  1. from flask import Flask, current_app
  2. app = Flask(__name__)
  3. # app上下文
  4. app_context = app.app_context()
  5. app_context.push()
  6. print('程序启动时执行:', current_app.name)
  7. @app.route('/')
  8. def index():
  9. print('接口请求访问时执行:', current_app.name) # 获取应用的名称
  10. return 'Hello world!'
  11. if __name__ == '__main__':
  12. app.run(debug=True)

运行结果:

示例代码2:  【with用法】

  1. from flask import Flask, current_app
  2. app = Flask(__name__)
  3. # app上下文, with用法
  4. with app.app_context():
  5. print('程序启动时执行:', current_app.name)
  6. @app.route('/')
  7. def index():
  8. print('接口请求访问时执行:', current_app.name) # 获取应用的名称
  9. return 'Hello world!'
  10. if __name__ == '__main__':
  11. app.run(debug=True)

运行结果:

示例代码:创建current_app_demo.py

  1. from flask import Flask, current_app
  2. app1 = Flask(__name__)
  3. app2 = Flask(__name__)
  4. # 以redis客户端对象为例
  5. # 用字符串表示创建的redis客户端
  6. # 为了方便在各个视图中使用,将创建的redis客户端对象保存到flask app中,
  7. # 后续可以在视图中使用current_app.redis_cli获取
  8. app1.redis_cli = 'app1 redis client'
  9. app2.redis_cli = 'app2 redis client'
  10. @app1.route('/route11')
  11. def route11():
  12. return current_app.redis_cli
  13. @app1.route('/route12')
  14. def route12():
  15. return current_app.redis_cli
  16. @app2.route('/route21')
  17. def route21():
  18. return current_app.redis_cli
  19. @app2.route('/route22')
  20. def route22():
  21. return current_app.redis_cli

运行

  1. export FLASK_APP=current_app_demo:app1
  2. flask run
  • 访问/route11 显示app1 redis client
  • 访问/route12 显示app1 redis client

 

  1. export FLASK_APP=current_app_demo:app2
  2. flask run
  • 访问/route21 显示app2 redis client
  • 访问/route22 显示app2 redis client

作用

current_app 就是当前运行的flask app,在代码不方便直接操作flask的app对象时,可以操作current_app就等价于操作flask app对象

2.2 g对象

        g 作为 flask 程序全局的一个临时变量,充当中间媒介的作用,我们可以通过它在一次请求调用的多个函数间传递一些数据。每次请求都会重设这个变量。

        g对象是线程隔离的。

保存为全局对象g对象的好处

        g对象是在整个Flask应用运行期间都是可以使用的。并且也跟request一样,是线程隔离的。

        这个对象是专门用来存储开发者自己定义的一些数据,方便在整个Flask程序中都可以使用。

        一般使用就是,将一些经常会用到的数据绑定到上面,以后就直接从g上面取就可以了,而不需要通过传参的形式,这样更加方便。

示例代码1:

  1. from flask import Flask, g
  2. app = Flask(__name__)
  3. def db_query():
  4. user_id = g.user_id
  5. user_name = g.user_name
  6. print('user_id={} user_name={}'.format(user_id, user_name))
  7. @app.route('/user/')
  8. def get_user_profile():
  9. g.user_id = 123
  10. g.user_name = 'dgw'
  11. db_query()
  12. return 'hello world'
  13. @app.route('/index/')
  14. def index():
  15. db_query()
  16. return '欢迎来到首页!'
  17. if __name__ == '__main__':
  18. app.run(debug=True)

运行结果:

2.3 g对象场景应用

有一个工具类utils.py 和 用户办理业务

示例代码1:

  1. # main.py
  2. from flask import Flask, request
  3. from utils import func1, func2, func3
  4. app = Flask(__name__)
  5. # Flask_线程隔离的g对象使用
  6. @app.route('/')
  7. def index():
  8. # 从url中取参数
  9. name = request.args.get('name')
  10. # 调用功能函数办理业务
  11. func1(name)
  12. func2(name)
  13. func3(name)
  14. return '办理业务成功'
  15. if __name__ == '__main__':
  16. app.run(debug=True)
  1. # utils.py
  2. def func1(name):
  3. print(f'func1 name:{name}')
  4. def func2(name):
  5. print(f'func2 name:{name}')
  6. def func3(name):
  7. print(f'func3 name:{name}')

运行结果:

 

示例代码2:

  1. # main.py
  2. from flask import Flask, request, g
  3. from utils import func1, func2, func3
  4. app = Flask(__name__)
  5. # Flask_线程隔离的g对象使用
  6. @app.route('/')
  7. def index():
  8. # 从url中取参数
  9. name = request.args.get('name')
  10. # 调用功能函数办理业务
  11. # 每次都得传参 麻烦,引入g对象进行优化
  12. g.name = name
  13. func1()
  14. func2()
  15. func3()
  16. return '办理业务成功'
  17. if __name__ == '__main__':
  18. app.run(debug=True)
  1. # utils.py
  2. # 优化工具类
  3. from flask import g
  4. def func1():
  5. print(f'func1 name:{g.name}')
  6. def func2():
  7. print(f'func2 name:{g.name}')
  8. def func3():
  9. print(f'func3 name:{g.name}')

运行结果:

2.4 g对象与请求钩子的综合案例

需求

  • 构建认证机制
  • 对于特定视图可以提供强制要求用户登录的限制
  • 对于所有视图,无论是否强制要求用户登录,都可以在视图中尝试获取用户认证后的身份信息

实现

  1. from flask import Flask, abort, g
  2. app = Flask(__name__)
  3. @app.before_request
  4. def authentication():
  5. """
  6. 利用before_request请求钩子,在进入所有视图前先尝试判断用户身份
  7. :return:
  8. """
  9. # TODO 此处利用鉴权机制(如cookie、session、jwt等)鉴别用户身份信息
  10. # if 已登录用户,用户有身份信息
  11. g.user_id = 123
  12. # else 未登录用户,用户无身份信息
  13. # g.user_id = None
  14. def login_required(func):
  15. def wrapper(*args, **kwargs):
  16. if g.user_id is not None:
  17. return func(*args, **kwargs)
  18. else:
  19. abort(401)
  20. return wrapper
  21. @app.route('/')
  22. def index():
  23. return 'home page user_id={}'.format(g.user_id)
  24. @app.route('/profile')
  25. @login_required
  26. def get_user_profile():
  27. return 'user profile page user_id={}'.format(g.user_id)
  28. app.run()

3、app_context 与 request_context

在Flask程序未运行的情况下,调试代码时需要使用current_appgrequest这些对象,会不会有问题?该如何使用?

3.1 app_context

app_context为我们提供了应用上下文环境,允许我们在外部使用应用上下文current_appg

可以通过with语句进行使用

  1. >>> from flask import Flask
  2. >>> app = Flask('')
  3. >>> app.redis_cli = 'redis client'
  4. >>>
  5. >>> from flask import current_app
  6. >>> current_app.redis_cli # 错误,没有上下文环境
  7. 报错
  8. >>> with app.app_context(): # 借助with语句使用app_context创建应用上下文
  9. ... print(current_app.redis_cli)
  10. ...
  11. redis client

示例代码:  【使用with,对app_context()返回一个APPContext对象】

  1. from flask import Flask, current_app
  2. app = Flask(__name__)
  3. print(app) # 输出结果:<Flask 'app'>
  4. with app.app_context():
  5. app2 = current_app
  6. print(app2) # 输出结果:<Flask 'app'>
  7. if __name__ == '__main__':
  8. app.run()

3.2 request_context

request_context为我们提供了请求上下文环境,允许我们在外部使用请求上下文requestsession

可以通过with语句进行使用

  1. >>> from flask import Flask
  2. >>> app = Flask('')
  3. >>> request.args # 错误,没有上下文环境
  4. 报错
  5. >>> environ = {'wsgi.version':(1,0), 'wsgi.input': '', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/', 'SERVER_NAME': 'itcast server', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '80'} # 模拟解析客户端请求之后的wsgi字典数据
  6. >>> with app.request_context(environ): # 借助with语句使用request_context创建请求上下文
  7. ... print(request.path)
  8. ...
  9. /
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/448734
推荐阅读
相关标签
  

闽ICP备14008679号