flask 中current_app._get_current_object()與current_app區別

在學習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.

這個問題的關鍵在倒數的第三句:

thr = Thread(target=send_async_email, args=[app, msg])

因爲這裏開了一個單獨的線程,也許你會奇怪了,憑什麼開了一個線程就不能使用 current_app 了?對!就是開了一個線程就不能使用 current_app。原因在於 current_app 的實現。

current_appFlask 是一個代理,如果你看 Flask 源碼的話會發現其實它外部包裹的是這樣的:
源碼地址 line: 48-58

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 的作用就是可以根據線程/協程返回對應當前協程/線程的對象,也就是說

  • 線程 A 往 LocalProxy 中塞入 A

  • 線程 B 往 LocalProxy 中塞入 B

無論在是什麼地方,

  • 線程 A 永遠取到得是 A,線程 B 取到得永遠是 B

這就是在 Flask 中可以在代碼中直接使用 requestcurrent_app 這樣的變量的底層原因。

所以,答案來了,因爲這裏開了一個新線程,如果你不穿真實對象過去,那麼你在線程裏面使用 current_app 將獲取不到對象,因爲他沒有 flask 上下文

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章