DjangoのChannelsで作る簡単なチャットアプリ(その1)

308, 2021-08-19

目次

DjangoのChannelsで作る簡単なチャットアプリ(その1)

DjangoではChannelsという機能を使うとWebSocketを使ったチャットアプリなどを作ることができます。
ChannelsはDjangoにおいて非同期通信なビューを実現するための機能です。

ChannelsはHTTPだけでなくさまざまなプロトコルに対応可能です。
たとえばWebSocket, MQTT, chatbots, アマチュア無線などです。

Channelsはチャネルと呼ばれるデータ構造が核となっており、これの実体はFIFOキューです。
論理的にはGoのchannelsと似ているらしいです。

この記事ではじっさいに簡単なチャットアプリを作りながら、DjangoのChannelsを見ていきます。
記事が長くなってしまったのでシリーズ化しました。あらかじめご了承ください。

Channelsのインストール

まず最初にpipdjangochannelsをインストールします。

$ pip install django
$ pip install channels

ChannelsはDjangoの機能(アプリケーション)なので、Djangoは必須になります。

サイトの作成

pipdjangoをインストールすると、環境でdjango-adminを使うことが出来るようになります。
django-adminでサイトを作ります。
今回はmysiteというサイト(プロジェクト)を作ります。

$ django-admin startproject mysite

channelsのインストール

mysite/settings.pyをエディタで開いて、INSTALLED_APPSを編集し、channelsをインストールします。
こうすることでDjangoでChannelsを使えるようになります。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels',  # <----------------- これ
]

asgi.pyの編集

Djangoは非同期通信ではasgi.pyを使います。
そのためmysite/asgi.pyを以下のように編集します。

# mysite/asgi.py
import os

from channels.routing import ProtocolTypeRouter
from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')

application = ProtocolTypeRouter({
    'http': get_asgi_application(),
    # その他のプロトコルもここに追加できる
})

この編集はhttpプロトコルにアプリケーションを対応させるためのものです。
その他のプロトコルについてもProtocolTypeRouterの引数を追加することで編集可能です。
今回はhttpプロトコルのみに対応します。

ASGI_APPLICATIONの定義

mysite/settings.pyの最後のほうにASGI_APPLICATIONを定義します。
ASGI_APPLICATIONの値はプロジェクト名.asgi.applicationになります。
今回はmysiteというサイト(プロジェクト)名にしているので↓のように定義しています。

...

ASGI_APPLICATION = 'mysite.asgi.application'

chatアプリの作成

manage.pyでアプリを作成します。
今回はチャットということなので、アプリ名はchatになっています。

$ python manage.py startapp chat

chatアプリを作成すると↓のようなディレクトリ構造になっていますが、今回はほとんど必要ありません。

chat
├── admin.py
├── apps.py
├── __init__.py
├── migrations
│         └── __init__.py
├── models.py
├── tests.py
└── views.py

1 directory, 7 files

そのため↓のようにファイルを掃除します。
使うのは__init__.pyviews.pyのみになります。

chat
├── __init__.py
└── views.py

0 directories, 2 files

chatアプリのインストール

chatアプリを作成したらmysite/settings.pyINSTALLED_APPSを再び編集します。
↓のようにchatアプリをインストールします。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels',
    'chat',  # <------------------- これ
]

インデックスページの作成

chatアプリのインデックスページを作成します。
chat以下にtemplates/chatディレクトリを作成してその中にindex.htmlを定義します。

chat
├── __init__.py
├── templates
│         └── chat
│             └── index.html  # <-------- これを作成
└── views.py

このindex.htmlページはチャットへの入室ページになります。
今回のチャットの設計では、「チャット・ルーム」という概念があります。
ユーザーはチャット・ルームに入室してチャットを行います。

index.htmlは↓のように定義します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>簡単なチャットへ入室する</title>
</head>
<body>
  <label>入室するチャットのルーム名: </label>
  <input id="room-name-input" type="text" size="100"> 
  <input id="room-name-submit" type="button" value="入室">

  <script>
    // チャット・ルームに入室するロジック
    const input = document.querySelector('#room-name-input')
    const submit = document.querySelector('#room-name-submit')

    input.focus()  // フォーカスを入力欄に設定する
    input.onkeyup = (ev) => {
      if (ev.keyCode === 13) {
        // ENTERキーが押されたら入室ボタンを押す
        submit.click()
      }
    }

    submit.onclick = (ev) => {
      // 入室ボタンがクリックされたら、入力されたチャット・ルーム名のページ(パス)に移動する
      window.location.pathname = '/chat/' + input.value + '/'
    }
  </script>
</body>
</html>

index.htmlではチャット・ルーム名の入力欄と入室ボタン、それからそれらのロジックを処理するスクリプトを書いています。

chat/views.pyの編集

chat/views.pyを↓のように編集します。

from django.shortcuts import render


def index(request):
    return render(request, 'chat/index.html')  # インデックスページを描画

indexというビューを作って、先ほど作成したtemplates/chat/index.htmlを描画します。

ルーティングの設定

次にパスとビューを繋げるためにルーティングの設定を行います。
まずmysite/urls.pyを↓のように編集します。

# mysite/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('chat/', include('chat.urls')),
]

/chat/へのアクセスはchaturls.pyに処理を任せています。
それからchat/urls.pyを作成し、↓のように編集します。

from django.urls import path
from chat.views import index

urlpatterns = [
    path('', index, name='index'),
]

ブラウザで動作確認

ここまでのコードが動くかどうか開発サーバーでテストします。
runserverコマンドで開発サーバーを起動します。

python manage.py runserver 0.0.0.0:9999

そしてブラウザでhttp://localhost:9999/chat/にアクセスします。
すると↓のようにブラウザの画面に表示されます。

index.png

参考

Django Channels — Channels 3.0.3 documentation