#はじめに
DjangoでWebSocketが使いたい時に、ググるとChannelsというライブラリが出てくると思います。
しかし、まだまだ公式ドキュメント以外の記事が少なく、ようやく見つけたと思ったらバージョンが1.xで記述が違う!ということに...
Channels 2.xを使った記事は本当に数少なく、さらにそれらの記事もチュートリアルをこなしたものだけという現状に絶望することになります。
この記事はWebSocketについての説明ではなく、チュートリアル以外のChannels 2.xの記事が増えることを願って、私が詰まったところと解決方法を残したいと思います。
環境
Python 3.6.0
Django 2.1.2
channels 2.1.5
#事前知識
いきなり丸投げですいませんが、とりあえずこちらの記事様を参考にしてチュートリアルをこなしていただければと思います。(その1からその4)
Django 2.0 + Channels 2.xを使ってWebSocketを扱う(その1)
http://www.denzow.me/entry/2018/03/25/235952
##redis
Channel Layerという複数のConsumer Instance間でメッセージを共有する仕組みがあります。この中のGroupという仕組みを使うことで、同じグループ内でメッセージのやりとりができます。
そしてChannel Layerを使うためにはsetting.pyに以下の記述が必要です。
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('redis', 6379)],
},
},
}
ここのhostにredisのサーバのホスト名と6379番ポート(多分redisのデフォルト)を記述します。
そして、__redisは別でインストール__しておきます。
(当然だろ、と思った方ばかりかとも思いますが、channel_redisを入れただけでいいと思っていたんですすいません)
Macであれば、brewでインストールできます。
試すだけならホスト名を"localhost"にして、ターミナルでredis-serverコマンドを叩けばredisが動きます。
ここを忘れていると、WebSocketがconnectしてからすぐに落ちます。注意
#やりたかったこと
私がやりたかったことは、スマートフォンからDjangoの方にデータをPOSTした時にWebSocketを介してWebページに自動で反映させるということです。
Channel 2.xではConsumerがクラスになっているのでViewからWebSocketを使うにはどうすればいいん?と詰まりました。
それではコードを
class Consumer(AsyncWebsocketConsumer):
async def connect(self):
# グループに入る
await self.channel_layer.group_add(
"test", # グループ名
self.channel_name # チャンネル名
)
await self.accept()
async def disconnect(self, close_code):
# グループから出る
await self.channel_layer.group_discard(
"test", # グループ名
self.channel_name # チャンネル名
)
async def chat_message(self, event):
message = event['message']
# メッセージを送る
await self.send(text_data=json.dumps({
'message': message
}))
view.pyは一部です。channelsを使う部分のみ
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
.
.
.
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)("test", { # グループ名
"type": "chat_message", # Consumerクラスのchat_messageメソッド
"message": list # 今回は[{},{},{}]のようなものを送った
})
.
.
.
まずconnect部分でグループに入ります。グループ名は同じグループにしたい人で共通。
そしてConsumer外でChannel Layerを使う方法についてです。
使いたい場所でget_channel_layer()
で取得して、group_send
で送ります。
group_send
はasync_to_sync
で囲んでおいてください。(非同期のため)
肝心な中身ですが、(グループ名,辞書型)になっています。
辞書の中身は、type
とmessage
が必要です。typeにはConsumerクラスで使うメソッド名(おそらく)、messageには送りたいものを記述します。
するとConsumerクラスのchat_message
にevent
として届きます。そこから送るメッセージを受け取り、あとはjsonでdumpしてWebSocketに流すだけです。
受け手のJS側は上記ページ(その2)を見ていただければ詰まることはないと思います。
onmessage()
で受け取ることができるかと思います。
#テンプレート内でのテーブル表示
ちなみにJSに送ったリストをテーブル表示させたいだけであれば、Tabulatorを使えばできます。
検索して見てください。バージョンによって書き方が違いますが、
(現行バージョンは4.1です)
↓こんな感じで使います。
var table = new Tabulator("#sample-table", {
data: message,
layout:"fitColumns",
columns:[
{title:"ID", field:"id", sortable:true},
{title:"テキスト", field:"text"},
{title:"時間", field:"date", sortable:true}
]
});
まあこの辺りは蛇足です。
#最後に
私はここまでくるのに何度も諦めそうになりました。
そのくらい記事がないのです。少しでも助けになれれば幸いです。
研究で使用したコードだったのでgithubにはあげてません。
ここまで見て頂きありがとうございました。