### 導航
- [索引](# "總目錄")
- [下一頁](# "即插視圖") |
- [上一頁](# "配置處理") |
- [Flask 0.10.1 文檔](#) ?
# 信號
0.6 新版功能.
從 Flask 0.6 開始, Flask 集成了信號支持。這個支持由 [blinker](http://pypi.python.org/pypi/blinker) [http://pypi.python.org/pypi/blinker] 庫提供,并且當它不可用時會優雅地退回。
什么是信號?信號通過發送發生在核心框架的其它地方或 Flask 擴展的動作時的通知來幫助你解耦應用。簡而言之,信號允許特定的發送端通知訂閱者發生了什么。
Flask 提供了幾個信號,其它的擴展可能會提供更多。另外,請注意信號傾向于通知訂閱者,而不應該鼓勵訂閱者修改數據。你會注意到,信號似乎和一些內置的裝飾器做同樣的事情(例如: [request_started](# "flask.request_started") 與[before_request()](# "flask.Flask.before_request") 十分相似)。然而它們工作的方式是有差異的。譬如核心的 [before_request()](# "flask.Flask.before_request") 處理程序以特定的順序執行,并且可以在返回響應之前放棄請求。相比之下,所有的信號處理器執行的順序沒有定義,并且不修改任何數據。
信號之于其它處理器最大的優勢是你可以在一秒鐘的不同的時段上安全地訂閱。譬如這些臨時的訂閱對單元測試很有用。比如說你想要知道哪個模板被作為請求的一部分渲染:信號允許你完全地了解這些。
### 訂閱信號
你可以使用信號的 [connect()](http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.connect "(在 Blinker v1.1)") [http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.connect] 方法來訂閱信號。該函數的第一個參數是信號發出時要調用的函數,第二個參數是可選的,用于確定信號的發送端。退訂一個信號,可以使用 [disconnect()](http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.disconnect "(在 Blinker v1.1)") [http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.disconnect]方法。
對于所有的核心 Flask 信號,發送端都是發出信號的應用。當你訂閱一個信號,請確保也提供一個發送端,除非你確實想監聽全部應用的信號。這在你開發一個擴展的時候尤其正確。
比如這里有一個用于在單元測試中找出哪個模板被渲染和傳入模板的變量的助手上下文管理器:
~~~
from flask import template_rendered
from contextlib import contextmanager
@contextmanager
def captured_templates(app):
recorded = []
def record(sender, template, context, **extra):
recorded.append((template, context))
template_rendered.connect(record, app)
try:
yield recorded
finally:
template_rendered.disconnect(record, app)
~~~
這可以很容易地與一個測試客戶端配對:
~~~
with captured_templates(app) as templates:
rv = app.test_client().get('/')
assert rv.status_code == 200
assert len(templates) == 1
template, context = templates[0]
assert template.name == 'index.html'
assert len(context['items']) == 10
~~~
確保訂閱使用了一個額外的 **extra 參數,這樣當 Flask 對信號引入新參數時你的調用不會失敗。
代碼中,從 with 塊的應用 app 中流出的渲染的所有模板現在會被記錄到templates 變量。無論何時模板被渲染,模板對象和上下文中都會被添加到它里面。
此外,也有一個方便的助手方法( [connected_to()](http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.connected_to "(在 Blinker v1.1)") [http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.connected_to] ),它允許你臨時地把函數訂閱到信號并使用信號自己的上下文管理器。因為這個上下文管理器的返回值不能由我們決定,所以必須把列表作為參數傳入:
~~~
from flask import template_rendered
def captured_templates(app, recorded, **extra):
def record(sender, template, context):
recorded.append((template, context))
return template_rendered.connected_to(record, app)
~~~
上面的例子會看起來是這樣:
~~~
templates = []
with captured_templates(app, templates, **extra):
...
template, context = templates[0]
~~~
Blinker API 變更
[connected_to()](http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.connected_to "(在 Blinker v1.1)") [http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.connected_to] 方法出現于 Blinker 1.1 。
### 創建信號
如果你想要在自己的應用中使用信號,你可以直接使用 blinker 庫。最常見的用法是在自定義的 [Namespace](http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Namespace "(在 Blinker v1.1)") [http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Namespace] 中命名信號。這也是大多數時候推薦的做法:
~~~
from blinker import Namespace
my_signals = Namespace()
~~~
現在你可以這樣創建新的信號:
~~~
model_saved = my_signals.signal('model-saved')
~~~
這里使用唯一的信號名,簡化調試。可以用 [name](http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.NamedSignal.name "(在 Blinker v1.1)") [http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.NamedSignal.name]屬性來訪問信號名。
給擴展開發者
如果你在編寫一個 Flask 擴展并且你想優雅地在沒有 blinker 安裝時退化,你可以用[flask.signals.Namespace](# "flask.signals.Namespace") 這么做。
### 發送信號
如果你想要發出信號,調用 [send()](http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.send "(在 Blinker v1.1)") [http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.send] 方法可以做到。它接受發送端作為第一個參數,和一些推送到信號訂閱者的可選關鍵字參數:
~~~
class Model(object):
...
def save(self):
model_saved.send(self)
~~~
永遠嘗試選擇一個合適的發送端。如果你有一個發出信號的類,把 self 作為發送端。如果你從一個隨機的函數發出信號,把 current_app._get_current_object()作為發送端。
傳遞代理作為發送端
永遠不要向信號傳遞 [current_app](# "flask.current_app") 作為發送端,使用current_app._get_current_object() 作為替代。這樣的原因是,[current_app](# "flask.current_app") 是一個代理,而不是真正的應用對象。
### 信號與 Flask 的請求上下文
信號在接收時,完全支持 [*請求上下文*](#) 。上下文本地的變量在[request_started](# "flask.request_started") 和 [request_finished](# "flask.request_finished") 一貫可用,所以你可以信任 [flask.g](# "flask.g") 和其它需要的東西。注意 [*發送信號*](#)和 [request_tearing_down](# "flask.request_tearing_down") 信號中描述的限制。
### 基于裝飾器的信號訂閱
你可以在 Blinker 1.1 中容易地用新的connect_via() 裝飾器訂閱信號:
~~~
from flask import template_rendered
@template_rendered.connect_via(app)
def when_template_rendered(sender, template, context, **extra):
print 'Template %s is rendered with %s' % (template.name, context)
~~~
### 核心信號
下列是 Flask 中存在的信號:
flask.template_rendered
當模板成功渲染的時候,這個信號會發出。這個信號與模板實例template 和上下文的字典(名為 context )一起調用。
訂閱示例:
~~~
def log_template_renders(sender, template, context, **extra):
sender.logger.debug('Rendering template "%s" with context %s',
template.name or 'string template',
context)
from flask import template_rendered
template_rendered.connect(log_template_renders, app)
~~~
flask.request_started
這個信號在處建立請求上下文之外的任何請求處理開始前發送。因為請求上下文已經被約束,訂閱者可以用 [request](# "flask.request") 之類的標準全局代理訪問請求。
訂閱示例:
~~~
def log_request(sender, **extra):
sender.logger.debug('Request context is set up')
from flask import request_started
request_started.connect(log_request, app)
~~~
flask.request_finished
這個信號恰好在請求發送給客戶端之前發送。它傳遞名為 response 的響應。
訂閱示例:
~~~
def log_response(sender, response, **extra):
sender.logger.debug('Request context is about to close down. '
'Response: %s', response)
from flask import request_finished
request_finished.connect(log_response, app)
~~~
flask.got_request_exception
這個信號在請求處理中拋出異常時發送。它在標準異常處理生效 *之前* ,甚至是在沒有異常處理的情況下發送。異常本身會通過 exception 傳遞到訂閱函數。
訂閱示例:
~~~
def log_exception(sender, exception, **extra):
sender.logger.debug('Got exception during processing: %s', exception)
from flask import got_request_exception
got_request_exception.connect(log_exception, app)
~~~
flask.request_tearing_down
這個信號在請求銷毀時發送。它總是被調用,即使發生異常。當前監聽這個信號的函數會在常規銷毀處理后被調用,但這不是你可以信賴的。
訂閱示例:
~~~
def close_db_connection(sender, **extra):
session.close()
from flask import request_tearing_down
request_tearing_down.connect(close_db_connection, app)
~~~
從 Flask 0.9 ,如果有異常的話它會被傳遞一個 exc 關鍵字參數引用導致銷毀的異常。
flask.appcontext_tearing_down
這個信號在應用上下文銷毀時發送。它總是被調用,即使發生異常。當前監聽這個信號的函數會在常規銷毀處理后被調用,但這不是你可以信賴的。
訂閱示例:
~~~
def close_db_connection(sender, **extra):
session.close()
from flask import request_tearing_down
appcontext_tearing_down.connect(close_db_connection, app)
~~~
如果有異常它會被傳遞一個 exc 關鍵字參數引用導致銷毀的異常。
flask.appcontext_pushed
這個信號在應用上下文壓入棧時發送。發送者是應用對象。這通常在單元測試中為了暫時地鉤住信息比較有用。例如這可以用來提前在 g 對象上設置一些資源。
用法示例:
~~~
from contextlib import contextmanager
from flask import appcontext_pushed
@contextmanager
def user_set(app, user):
def handler(sender, **kwargs):
g.user = user
with appcontext_pushed.connected_to(handler, app):
yield
~~~
測試代碼:
~~~
def test_user_me(self):
with user_set(app, 'john'):
c = app.test_client()
resp = c.get('/users/me')
assert resp.data == 'username=john'
~~~
0.10 新版功能.
flask.appcontext_popped
這個信號在應用上下文彈出棧時發送。發送者是應用對象。這通常在appcontext_tearing_down 信號發送后發送。
0.10 新版功能.
flask.message_flashed
這個信號在應用對象閃現一個消息時發送。消息作為 message 命名參數發送,分類則是 category 參數。
訂閱示例:
~~~
recorded = []
def record(sender, message, category, **extra):
recorded.append((message, category))
from flask import message_flashed
message_flashed.connect(record, app)
~~~
0.10 新版功能.
? 版權所有 2013, Armin Ronacher.
- 歡迎使用 Flask
- 前言
- 給有經驗程序員的前言
- 安裝
- 快速入門
- 教程
- 介紹 Flaskr
- 步驟 0: 創建文件夾
- 步驟 1: 數據庫模式
- 步驟 2: 應用設置代碼
- 步驟 3: 創建數據庫
- 步驟 4: 請求數據庫連接
- 步驟 5: 視圖函數
- 步驟 6: 模板
- 步驟 7: 添加樣式
- 福利: 應用測試
- 模板
- 測試 Flask 應用
- 記錄應用錯誤
- 配置處理
- 信號
- 即插視圖
- 應用上下文
- 請求上下文
- 用藍圖實現模塊化的應用
- Flask 擴展
- 與 Shell 共舞
- Flask 代碼模式
- 大型應用
- 應用程序的工廠函數
- 應用調度
- 使用 URL 處理器
- 部署和分發
- 使用 Fabric 部署
- 在 Flask 中使用 SQLite 3
- 在 Flask 中使用 SQLAlchemy
- 上傳文件
- 緩存
- 視圖裝飾器
- 使用 WTForms 進行表單驗證
- 模板繼承
- 消息閃現
- 用 jQuery 實現 Ajax
- 自定義錯誤頁面
- 延遲加載視圖
- 在 Flask 中使用 MongoKit
- 添加 Favicon
- 數據流
- 延遲請求回調
- 添加 HTTP Method Overrides
- 請求內容校驗碼
- 基于 Celery 的后臺任務
- 部署選擇
- mod_wsgi (Apache)
- 獨立 WSGI 容器
- uWSGI
- FastCGI
- CGI
- 聚沙成塔
- API
- JSON 支持
- Flask 中的設計決策
- HTML/XHTML 常見問題
- 安全注意事項
- Flask 中的 Unicode
- Flask 擴展開發
- Pocoo 風格指引
- Python 3 支持
- 升級到最新版本
- Flask Changelog
- 許可證
- 術語表