web-dev-qa-db-ja.com

Django Channels 2でグループにメッセージを送信する

チャンネル2で動作するようにグループメッセージングを取得できないという点で、私は完全に立ち往生しています!私は見つけることができるすべてのチュートリアルとドキュメントに従いましたが、残念ながら、まだ問題が何であるかはわかりませんでした。私が今やろうとしていることは、訪問したときに「イベント」という名前のグループに簡単なメッセージをブロードキャストする特定のURLを持つことです。

まず最初に、Djangoで採用している関連する現在の設定を以下に示します。

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            'hosts': [('localhost', 6379)],
        },
    }
}

ASGI_APPLICATION = 'backend.routing.application'

INSTALLED_APPS = [
    'Django.contrib.admin',
    'Django.contrib.auth',
    'Django.contrib.contenttypes',
    'Django.contrib.sessions',
    'Django.contrib.messages',
    'Django.contrib.staticfiles',
    'rest_framework',
    'corsheaders',
    'channels',
    'channels_redis',
    'backend.api'
]

次に、非常に基本的な方法でJsonWebsocketConsumerを拡張するEventConsumerを示します。これは、メッセージを受信したときにエコーバックするだけです。そのため、単純なsend_json応答が到着するはずですが、機能しないのはグループブロードキャストのみです。

class EventConsumer(JsonWebsocketConsumer):
    groups = ["events"]

    def connect(self):
        self.accept()

    def disconnect(self, close_code):
        print("Closed websocket with code: ", close_code)
        self.close()

    def receive_json(self, content, **kwargs):
        print("Received event: {}\nFrom: {}\nGroups: 
                               {}".format(content, 
                                          self.channel_layer, 
                                          self.groups))

        self.send_json(content)

    def event_notification(self, event):
        self.send_json(
            {
                'type': 'test',
                'content': event
            }
        )

次に、ブロードキャストをトリガーするURLのURL構成を示します。

プロジェクトurls.py

from backend.events import urls as event_urls

urlpatterns = [
    url(r'^events/', include(event_urls))
]

イベントアプリurls.py

from backend.events.views import alarm

urlpatterns = [
    url(r'alarm', alarm)
]

そして最後に、グループブロードキャストが行われるビュー自体:

from Django.shortcuts import HttpResponse
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync


def alarm(req):
    layer = get_channel_layer()
    async_to_sync(layer.group_send)('events', {'type': 'test'})
    return HttpResponse('<p>Done</p>')
10
Måns Thörnvik

この質問を書いている間に解決策を見つけ、他の誰かがそれを利用するかもしれないと考えました!ここにある質問のほとんどは2.0以前のバージョンのチャネルに関するものであるため、これはコンシューマでgroup_sendイベントを処理する方法です。

問題はgroup_send関数の使用方法だけではありませんでしたが、EventConsumerにグループクラス変数を追加すると、そのグループに自動的に追加されると誤って仮定していました。 connectクラス関数でグループを手動で追加し、disconnect関数でグループを削除する必要があります!

また、問題は、私のコンシューマーに適切なイベントハンドラーが指定されていないことにもありました。アラーム要求が取り込まれるビューファイルでは、「タイプ」を「テスト」に設定していました。 EventConsumerクラスにテストが反映されなかったため、イベントを処理できませんでした。マルチチャットの例 here の行番号146に記載されているように、ヘルパー関数は送信されたイベントのタイプに応じて呼び出されます。したがって、 'event.alarm'のイベントタイプには、コンシューマでevent_alarmの対応する関数が必要です。シンプルですが、あまり文書化されていません:)。最終的なソリューションは次のとおりです。

consumers.pyで、接続中のgroup_addと切断中のgroup_discardに注意してください!

class EventConsumer(JsonWebsocketConsumer):

    def connect(self):
        async_to_sync(self.channel_layer.group_add)(
            'events',
            self.channel_name
        )
        self.accept()

    def disconnect(self, close_code):
        print("Closed websocket with code: ", close_code)
        async_to_sync(self.channel_layer.group_discard)(
            'events',
            self.channel_name
        )
        self.close()

    def receive_json(self, content, **kwargs):
        print("Received event: {}".format(content))
        self.send_json(content)

    # ------------------------------------------------------------------------------------------------------------------
    # Handler definitions! handlers will accept their corresponding message types. A message with type event.alarm
    # has to have a function event_alarm
    # ------------------------------------------------------------------------------------------------------------------

    def events_alarm(self, event):
        self.send_json(
            {
                'type': 'events.alarm',
                'content': event['content']
            }
        )

したがって、上記の関数events_alarmは、次のgroup_sendから呼び出されます。

from Django.shortcuts import HttpResponse

from channels.layers import get_channel_layer

from asgiref.sync import async_to_sync


def alarm(req):
    layer = get_channel_layer()
    async_to_sync(layer.group_send)('events', {
        'type': 'events.alarm',
        'content': 'triggered'
    })
    return HttpResponse('<p>Done</p>')

質問/回答をさらに明確にする必要がある場合はお知らせください!乾杯!

25
Måns Thörnvik

私のgroup_sendが機能しなかった理由は、websocketが実際に接続されていなかったからです。

テスト時にソケットを切断したdevサーバーがリロードされたため、その後の呼び出しはコンシューマーによって実行されませんでした。フロントエンドを更新するとソケットが再接続され、group_sendが機能し始めました。

これは質問に直接対処するものではありませんが、これが誰かの助けになることを願っています。

1
Richard Pryce