flask工作原理图

  • 整体过程

  一个请求进入flask框架后, flask会首先实例化一个Request Context封装了这次请求的相关信息(Request), 然后将请求上下文推入栈_request_ctx_stack(这是LocalStack的一个实例).

  在RequestContext对象入栈之前会检查App Context对应栈栈顶的元素, 如果不是当前的app, 则会先将app推入. 因此如果在一个请求中使用(注意是在请求中)使用current_app是不需要手动push的.

  current_app取得是_app_ctckx_stack 的栈顶元素中的app属性, 这个属性就是我们自己创建的app=Flask(__name__), 如果栈顶为空,则提示unbound, 同样的request指的是_request_ctx_stack的栈顶对应对象, 当一个请求结束的时候会出栈.

  Local 与 LocalStack: werkzeug, LocalStack作为线程隔离对象栈的基本特性

其他

  • 手动压栈和出栈

如果要在没有请求的情况下使用核心对象需要手动push和pop, 一个例子

ctx = app.app_context()
ctx.push() # 入栈, push是LocalStack中的方法
# 其他语句
ctx.pop()  # 出栈

以上代码等同于:

with app.app_context():
    # 其他语句

比如在异步发送邮件的时候:

from flask import render_template, current_app
from flask_mail import Message
from threading import Thread
from app.ext import mail

def async_send_mail(app, msg):
    #获取当前程序的上下文
    with app.app_context():
        mail.send(message=msg)


def send_mail(subject, to, tem, **kwargs):
    app = current_app._get_current_object()
    msg = Message(
        subject=subject, recipients=[to], sender=app.config['MAIL_USERNAME'])
    msg.html = render_template('email/' + tem + '.html', **kwargs)
    send = Thread(target=async_send_mail, args=(app, msg))
    send.start()


  • 值得说明

Local使用字典的方式实现的线程隔离,
localStack封装了Local对象, 把Local对象作为自己的属性从而实现了线程隔离的栈结构
_request_ctx_stack和_app_ctx_stack 都是LocalStack的实例(在flask源码的globals.py末尾可以找到)

_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)

Flask: 核心对象, 承载了各种功能, 比如配置信息,注册路由的信息等
AppContext: 对核心对象的封装, 同时附加了额外的参数
Request: 请求信息, 比如url相关的参数
RequestContext: 对Request对象的封装

  • 参考资料
  1. Flask 的 Context 机制

  2. flask高级编程(鱼书)第五章, 第六章