資料綁定¶
Channel 的資料綁定架構會自動處理 Django 的 model 寫入前端 view 中,例如使用 javascript 強化的網站。它提供了一個快速且彈性的方式來產生 Group 的 model 改變 message,以及接收 model 發生變化時的 message。
當前主要的目標是 WebSocket,但此架構有相當的彈性可以支援任何通訊協定。
資料綁定可以接受什麼?¶
Channel 的資料綁定以兩種方式運作:
發送,當 model 透過 Django 發生變化時,訊息會發送到監聽的客戶端。這包含了事例的建立、更新與刪除。
接收,標準化的訊息格式,允許客戶端發送訊息來建立、更新與刪除事例。
收發,允許 UI 可以設計成自動更新反映客戶端更新的數值。例如,網誌的即時更新可以藉由 PO 文物件的資料綁定來達成,而編輯介面也可以藉此同步顯示其他使用者的修改。
It has some limitations:
發送的資料綁定是藉由 signal 來達成的,所以假使 model 的資料更新不是透過 Django (或是使用 QuerySet 的
.update()
函式),就沒有觸發的 signal,改變的訊息就不會被送出。你可以自己觸發改變,但是你會需要從系統中正確的來源來送出這個 signal。內建的序列化是來自 Django 的內建功能,它只能處理特定的資料型態。如果需要有更大的彈性,你可以透過像是 Django REST 架構的序列化函式庫來達成。
入門¶
單一的綁定子類別用來處理 model 發送與接收的綁定,你也可以在每個 model 使用多個綁定 (例如如果你想使用不同的格式或權限檢查)。
你可以自底層的 Binding 實作所有需要的函式,但我們這裡把重點放在 WebSocket JSON 變形上,因著這是最簡單的入手點而且最接近你可能需要的部分。
從這裡開始:
from django.db import models
from channels.binding.websockets import WebsocketBinding
class IntegerValue(models.Model):
name = models.CharField(max_length=100, unique=True)
value = models.IntegerField(default=0)
class IntegerValueBinding(WebsocketBinding):
model = IntegerValue
stream = "intval"
fields = ["name", "value"]
@classmethod
def group_names(cls, instance):
return ["intval-updates"]
def has_permission(self, user, action, pk):
return True
這裡定義一個 WebSocket 的綁定 - 如此就知道如何送出 JSON WebSocket 格式的頁框 - 並且提供三件你必須提供的部分:
fields
是一個序列化請求可傳回欄位的白名單。Channel 預設不開啟所有的欄位,主要是基於安全性的考量。如果你想全部開啟的話,把該列表設為["__all__"]
即可。另一方便,也可以使用exclude
來建立黑名單。group_names
傳回一個基於該事例的外送更新群組列表。例如,你可以發送PO文到名稱包含父網誌 ID 的不同即時網誌中。這裡我們只用一個固定的群組名稱。基於group_names
如何隨著事例的改變,Channels 將會處理客戶端需要的create
,update
或delete
等訊息 (或是改變是對客戶端隱藏的)。has_permission
則傳回一個接收綁定更新,是否會被 model 執行的許可與否。我們採取了一個非常不安全的作法,總是回傳True
。但是這裡就是你可以讓 Django 做檢查或是自行撰寫權限系統的地方。
做為參考, action
總是以下 "create"
, "update"
或 "delete"
之一的萬國碼字串。你也可以提供 WebSocket Multiplexing 串流名稱給客戶端,如果使用 WebSocket 資料綁定,你必須使用多工化。
只要如此新增一個綁定在匯入的地方,發送綁定訊息就會被送出,但你仍需要提供一個 Consumer 來接受進來的綁定更新,並且在連線時將人加到正確的群組。WebSocket 綁定類別使用標準的 WebSocket Multiplexing ,因此你只需要使用它。
from channels.generic.websockets import WebsocketDemultiplexer
from .binding import IntegerValueBinding
class Demultiplexer(WebsocketDemultiplexer):
consumers = {
"intval": IntegerValueBinding.consumer,
}
def connection_groups(self):
return ["intval-updates"]
如同標準的串流對消費者映射,你也需要指定 connection_groups
,一個將上線使用者加入群組的列表。這也符合 group_names
在你的綁定上的邏輯,這裡我們使用一個固定的群組名稱。請注意,綁定有一個 .consumer
屬性,這是一個標準 WebSocket-JSON consumer,解多工器可以發送解開的 websocket.receive
訊息給這個 consumer。
綁到你的路由,這樣子就完成了:
from channels import route_class, route
from .consumers import Demultiplexer
from .models import IntegerValueBinding
channel_routing = [
route_class(Demultiplexer, path="^/binding/"),
]
前端的考量點¶
You can use the standard Channels WebSocket wrapper to
automatically run demultiplexing, and then tie the events you receive into your
frontend framework of choice based on action
, pk
and data
.
備註
我們需要熱門 JavaScript 架構的資料綁定插件,如果你有興趣提供,請和我們聯絡。
客製序列化/通訊協定¶
不同於繼承自 WebsocketBinding
,你可以直接繼承自底層的 Binding
類別,然後自己實作序列化與反序列化。在這部分的參考文件完成之前,我們建議參考 channels/bindings/base.py
原始碼,程式中有相當完整的註解。
斷線的處理¶
由於 Channel 的資料綁定沒有包含事件的歷史,也就是說當網路連線斷開,你會遺失這段時間發生的事例訊息。因此,建議當連線恢復之後,直接透過 API 來重新載入資料,而不要依賴即時更新在關鍵的功能,或是設計 UI 來處理資料遺失的問題。(例如只有更新沒有新建時,下個更新會修正全部的遺失資料)