在学习flask开发,书中一段异步发送邮件的代码是这样写的:
from threading import Thread
from flask import current_app, render_template
from flask.ext.mail import Message
from . import mail
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
def send_email(to, subject, template, **kwargs):
app = current_app._get_current_object()
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
thr = Thread(target=send_async_email, args=[app, msg])
thr.start()
return thr
在send_mail
函数中,程序使用了current_app._get_current_object()
赋值给app
作为当前程序的实例。此处为什么不直接使用current_app
呢?
flask官方文档中是这样解释这个方法_get_current_object()
的:
Return the current object. This is useful if you want the real object behind the proxy at a time for performance reasons or because you want to pass the object into a different context.
看了这个我还是没懂书中的代码为什么要这样= =。。。
这个问题的关键在倒数的第三句:
因为这里开了一个单独的线程,也许你会奇怪了,凭什么开了一个线程就不能使用
current_app
了?对!就是开了一个线程就不能使用current_app
。原因在于current_app
的实现。current_app
在Flask
是一个代理,如果你看Flask
源码的话会发现其实它外部包裹的是这样的:源码地址 line: 48-58
这个
LocalProxy
就不展开讲了,但是我可以告诉你这个LocalProxy
的作用就是可以根据线程/协程返回对应当前协程/线程的对象,也就是说线程 A 往 LocalProxy 中塞入 A
线程 B 往 LocalProxy 中塞入 B
无论在是什么地方,
线程 A 永远取到得是 A,线程 B 取到得永远是 B
这就是在
Flask
中可以在代码中直接使用request
、current_app
这样的变量的底层原因。所以,答案来了,因为这里开了一个新线程,如果你不穿真实对象过去,那么你在线程里面使用
current_app
将获取不到对象,因为他没有flask 上下文
。这又是一个 Flask 的重要概念,太多得说。大概可以这样说,题主应该可以理解吧。