LoginSignup
1
5

More than 5 years have passed since last update.

DjangoとVueでカンバンアプリケーションを作る(6)

Last updated at Posted at 2018-09-22

※はじめからはこちら

今回からいよいよ、Django Channelsを使ってWebsocket部分を実装していきます。今回はサーバサイドを大まかに作っていきます。

Django Channels

Django Channelsは標準のDjangoでは扱いづらいWebsocketなどのプロトコルへDjangoを対応させるためのライブラリです。

Django開発グループが作成しているので安心感があります。今後DjangoでWebsocketを使う必要がある場合はChannels一択になるかと思います。

channelsの有効化

今回利用しているDockerの環境にはChannelsはインストール済ですので不要ですが、実際にインストールする場合は以下を実行します。

今回は実施済なので不要
$ pip install channels channels-redis

さて、インストールされているchannelsを有効化ていきます。

まずは、channelsのエントリーポイント(最初にアクセスされるファイル)を用意します。

application/views/routing.py
from channels.routing import ProtocolTypeRouter


application = ProtocolTypeRouter({})

ProtocolTypeRouterが最初にアクセスを受け入れる入り口になります。その名前の通りアクセスしてきたプロトコルごとに処理を振り分けます。今はどのプロトコルに対しても振り分けのルールを書いていませんが、デフォルトでHTTP用のルールが自動設定されているので、まずはこのままで大丈夫です。

続いてsettingsを変更していきます。

application/settings/base.py

index 264d64a..23dc6b3 100644
--- a/application/settings/base.py
+++ b/application/settings/base.py
@@ -39,6 +39,9 @@ INSTALLED_APPS = [
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
+
+    'channels',
+
     'modules.kanban',
 ]

@@ -133,3 +136,6 @@ ROOT_URLCONF = 'application.views.urls'
 LOGIN_REDIRECT_URL = '/'
 LOGIN_URL = '/accounts/login/'
 LOGOUT_REDIRECT_URL = '/'
+
+# ASGIの起点を指定
+ASGI_APPLICATION = 'views.routing.application'

INSTALLED_APPSchannels'django.contrib.staticfiles''modules.kanban'の間に追加します。そして、ASGI_APPLICATIONという変数に、先程追加したrouting.pyapplicationを指定します。

場合によっては編集中にdjangoが再起動して、中途半端な状態でファイルを読み込み停止してしまう可能性もあるので動きがおかしいと思った場合は以下でDockerを再起動します。

$ docker-compose restart
実行例
$ docker-compose restart
WARNING: The DJANGO_ENV variable is not set. Defaulting to a blank string.
Restarting workrepo-for-tutorial_service_nginx_1 ... done
Restarting workrepo-for-tutorial_service_1       ... done
Restarting workrepo-for-tutorial_vuejs_1         ... done
Restarting workrepo-for-tutorial_redis_1         ... done
Restarting workrepo-for-tutorial_db_1            ... done

再起動後、 http://localhost:3000/ に再度アクセスして画面がしっかりと表示されてば設定は完了です。見た目上は特に変わっていませんが、Channels上(ASGI上)でアプリケーションが動作するようになっています。

websocketの処理追加

Channels上でWebSocketを使えるようにしていきます。このセクションではws://localhost:3000/ws/board/1へWebsocketでアクセスできるようにします。

Consumerの作成

いままではAPIごとにViewを作ってきましたが、WebSocketでも同じ様に処理を受け付けるコンポーネントはConsumerと呼ばれます。まずは簡単なConsumerを作っておきます。

views配下に新たにwsパッケージを作ります。

$ mkdir application/views/ws
$ touch application/views/ws/__init__.py

続いて、以下のファイルを作成します。

application/views/ws/kanban_consumer.py
from channels.generic.websocket import JsonWebsocketConsumer


class KanbanConsumer(JsonWebsocketConsumer):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 自身に一意なIDを付与する
        self.consumer_id = id(self)
        # 後で使う
        self.user = None

    def connect(self):
        # 認証チェック
        if not self.scope['user'].is_authenticated:
            self.close()
            return

        self.user = self.scope['user']
        # 接続を受け入れる
        self.accept()

自身のConsumerを定義する場合はchannelsが提供しているベースクラスを継承して作成します。JSON以外を扱う前提の基底クラスもありますが、JSONをクライアントとやり取りするのであればJsonWebsocketConsumerを継承しておくのが最も楽です。

__init__ はオーバーライドする必要は必ずしもないですが、Consumerに独自の属性(consumer_id, user)をもたせたいので今回はオーバライドしています。

websocketの場合、最初にアクセスしてきた際にこのKanbanConsumerがインスタンス化され、自身のconnectメソッドが呼び出されます。今回は、そのタイミングでログイン済であるかをチェックし、未ログインであればself.close()を呼び出して切断するようにしています。

routingの追加

先程作成したrouting.pyを以下のように編集します。

application/views/routing.py
@@ -1,4 +1,13 @@
-from channels.routing import ProtocolTypeRouter
+from channels.auth import AuthMiddlewareStack
+from channels.routing import ProtocolTypeRouter, URLRouter
+from django.urls import path

+from .ws.kanban_consumer import KanbanConsumer

-application = ProtocolTypeRouter({})
+application = ProtocolTypeRouter({
+    'websocket': AuthMiddlewareStack(
+        URLRouter([
+            path('ws/boards/<int:board_id>', KanbanConsumer),
+        ])
+    ),
+})

websocketというキーにマッピングを追加しましたので、WebSocketでのアクセスはここに流れてきます。まずは、AuthMiddlewareStackが呼び出されて、Django自体が提供しているセッション内のログインユーザ情報にChannelsでもアクセスできるようになります。その後、リクエストされたURLが'ws/boards/<int:board_id>'にマッチすれば、先程作成されたKanbanConsumerが呼び出されるようになります。

動作確認

WebSocketの場合、ブラウザから開いて動作確認というのは難しいです(ブラウザから開く場合はHTTPになるので)。wscatなどを使うことが多いようですが、今回はちゃんとログインしたセッションでないと処理できないので、wsrequestsというPythonライブラリを使って確認します。

これはカンバンアプリケーションの動作に必要なライブラリではないですが、動作確認のためにインストールします。(Docker外で大丈夫です)

$ pip install wsrequests

そして、wstest.pyとして以下のファイルを作成します。もしログイン情報をtest/test以外にしている場合は書き換えてください。

wstest.py
from wsrequests import WsRequests

wsr = WsRequests()

# login django
wsr.get('http://localhost:3000/accounts/login/')
wsr.post(
    'http://localhost:3000/accounts/login/',
    data={
        'username': 'test',  # Djangoのユーザ名とパスワード
        'password': 'test',
        'csrfmiddlewaretoken': wsr.cookies['csrftoken'],
        'next': '/',
    }
)
wsr.connect('ws://localhost:3000/ws/boards/1')
wsr.disconnect()

そして以下のように実行します。

$ python wstest.py
実行例
$ python wstest.py
2018-09-22 17:00:13,528 - DEBUG - MainThread - connect websocket [ws://localhost:3000/ws/boards/1]
2018-09-22 17:00:13,545 - DEBUG - MainThread - disconnect websocket [ws://localhost:3000/ws/boards/1]

実行例のように、エラーなくconnectdisconnectが出れば正常です。ここまででChannelsをつかったConsumerの基本的な部分が用意できましたので、次回からは実際の処理を組み込んでいきます。

次回

1
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
5