web-dev-qa-db-ja.com

Djangoチャネルを使用して単一のユーザーにメッセージを送信する

私は Django-channels を試し、ドキュメントを読んだり、サンプルをいじったりしています。

新しいインスタンスをデータベースに保存することによってトリガーされる単一のユーザーにメッセージを送信できるようにしたい。

私の使用例は、(セロリタスクを介して)新しい通知を作成し、通知が保存されたら、この通知を1人のユーザーに送信します。

これは可能であるようです( Django-channels docs から)

...重要な部分は、任意のイベントに応答してコードを実行できる(チャネルで送信できる)ことです。これには、作成したものも含まれます。モデルの保存、他の受信メッセージ、またはビューやフォーム内のコードパスからトリガーできます。

しかし、ドキュメントをさらに読み、 Django-channelsの例 をいじってみると、これを行う方法がわかりません。データバインディングとliveblogの例はグループへの送信を示していますが、単一のユーザーに送信するだけの方法はわかりません。

任意の提案をいただければ幸いです。

11
lukeaus

その特定のユーザーのグループを作成するという@Flipの答えを拡張します。

python ws_connect関数内の関数で、そのユーザーをグループのためにグループに追加できます:

consumers.py

from channels.auth import channel_session_user_from_http
from channels import Group

@channel_session_user_from_http
def ws_connect(message):
    if user.is_authenticated:
        Group("user-{}".format(user.id)).add(message.reply_channel)

そのユーザーにpythonコードからのメッセージを送信するには:

my view.py

import json
from channels import Group

def foo(user):
    if user.is_authenticated:
        Group("user-{}".format(user.id)).send({
            "text": json.dumps({
            "foo": 'bar'
        })
    })

接続されている場合は、メッセージを受け取ります。ユーザーがWebSocketに接続していない場合、サイレントで失敗します。

また、1人のユーザーのみを各ユーザーのグループに接続するようにする必要があります。そうしないと、複数のユーザーが特定のユーザーのみを対象としたメッセージを受信する可能性があります。

ルーティングを実装する方法、クライアント側でWebSocket接続を作成する方法、Django_channelsを設定する方法については、Djangoチャネルの例、特に multichat をご覧ください。

Djangoチャネルのドキュメント も確認してください。

16
lukeaus

チャネル2でのグループの動作はチャネル1での動作とは異なるため、更新はほとんどありません ここ のように、グループクラスはもうありません。

新しいグループAPIはドキュメント化されています herehere も参照してください。

私にとってうまくいくのは:

# Required for channel communication
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync


def send_channel_message(group_name, message):
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        '{}'.format(group_name),
        {
            'type': 'channel_message',
            'message': message
        }
    )

コンシューマーでメッセージタイプを処理するメソッドを定義することを忘れないでください!

    # Receive message from the group
    def channel_message(self, event):
        message = event['message']

        # Send message to WebSocket
        self.send(text_data=json.dumps({
            'message': message
        }))
10
user42488

最善の方法は、その特定のユーザーのグループを作成することです。 ws_connectを実行すると、そのユーザーをGroup("%s" % <user>).add(message.reply_channel)に追加できます

注:私のWebSocket URLはws://127.0.0.1:8000/<user>

4
Raja Simon

Channels 2では、self.channel_nameを各ユーザーに固有のハッシュであるdb on connectメソッドに保存できます。 ドキュメントはこちら

from asgiref.sync import async_to_sync
from channels.generic.websocket import AsyncJsonWebsocketConsumer
import json

class Consumer(AsyncJsonWebsocketConsumer):
    async def connect(self):
        self.room_group_name = 'room'

        if self.scope["user"].is_anonymous:
            # Reject the connection
            await self.close()
        else:
            # Accept the connection
            await self.channel_layer.group_add(
                self.room_group_name,
                self.channel_name
            )

            await self.accept()

        print( self.channel_name )

最後の行はspecific.WxuYsxLK!owndoeYTkLBwのようなものを返します

ユーザーのテーブルに保存できるこの特定のハッシュ。

1
Karl Zillner

@luke_ausの回答を拡張するために、ResourceBindingsを使用している場合は、オブジェクトを「所有している」ユーザーだけがこれらの更新を取得するように設定することもできます。

@luke_ausの回答と同じように、そのユーザーにのみ表示されるアクション(updatecreate)などを公開できる独自のグループにユーザーを登録します。

from channels.auth import channel_session_user_from_http,
from channels import Group

@channel_session_user_from_http
def ws_connect(message):
    Group("user-%s" % message.user).add(message.reply_channel)

次のようなモデルを想定して、バインドされたオブジェクトがそのユーザーに属している場合にのみ変更を公開するように、対応するバインディングを変更できます。

class SomeUserOwnedObject(models.Model):
    owner = models.ForeignKey(User)

これで、このモデルをユーザーグループにバインドでき、すべてのアクション(更新、作成など)は、この1人のユーザーにのみ公開されます。

class SomeUserOwnedObjectBinding(ResourceBinding):
    # your binding might look like this:
    model = SomeUserOwnedObject
    stream = 'someuserownedobject'
    serializer_class = SomeUserOwnedObjectSerializer
    queryset = SomeUserOwnedObject.objects.all()

    # here's the magic to only publish to this user's group
    @classmethod
    def group_names(cls, instance, action):
        # note that this will also override all other model bindings
        # like `someuserownedobject-update` `someuserownedobject-create` etc
        return ['user-%s' % instance.owner.pk]
0
devsnd