在學(xué)習(xí)Flask的時候,《Flask Web開發(fā)》這本書中有一個異步發(fā)送email的例子,
其中用到了線程
from . import mail,create_app
def send_async_email(app,msg):
with app.app_context():
mail.send(msg)
def send_email(to,subject,template,**kwargs):
msg = Message(current_app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,
sender=current_app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.txt', **kwargs)
#with current_app.app_context():
# mail.send(msg)
thr = Thread(target=send_async_email, args=(current_app, msg))
thr.start()
return thr
發(fā)送郵件總是提示錯誤
RuntimeError: Working outside of application context.
后來查找資料才知道是傳遞current_app的問題
current_app在 Flask是一個代理,如果你看 Flask源碼的話會發(fā)現(xiàn)其實它外部包裹的是這樣的:
def _find_app():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError(_app_ctx_err_msg)
return top.app
...
current_app = LocalProxy(_find_app)
這個 LocalProxy就不展開講了霞揉,但是我可以告訴你這個LocalProxy的作用就是可以根據(jù)線程/協(xié)程返回對應(yīng)當前協(xié)程/線程的對象霹娄,也就是說
線程 A 往 LocalProxy 中塞入 A
線程 B 往 LocalProxy 中塞入 B
無論在是什么地方,線程 A 永遠取到得是 A旺聚,線程 B 取到得永遠是 B
這就是在 Flask中可以在代碼中直接使用 request、current_app這樣的變量的底層原因眶蕉。
所以砰粹,因為這里開了一個新線程,如果你不傳真實對象過去造挽,那么你在線程里面使用 current_app將獲取不到對象碱璃,因為他沒有 flask 上下文。
獲取真實對象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.
修改send_email函數(shù)后代碼如下:
def send_email(to,subject,template,**kwargs):
msg = Message(current_app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,
sender=current_app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.txt', **kwargs)
#with current_app.app_context():
# mail.send(msg)
#current_app只是一個代理獲取而已,傳遞給其它子線程獲取到的依然是子線程的上下文
# 必須_get_current_object線獲取到原始對象再傳遞過去
app = current_app._get_current_object()
thr = Thread(target=send_async_email, args=(app, msg))
thr.start()
return thr
這樣就能異步發(fā)送郵件成功了