Отправка сообщения одному пользователю с помощью django-каналов

Я пробовал django-каналы включая чтение документов и игру с примерами.

Я хочу иметь возможность отправлять сообщение одному пользователю, которое запускается при сохранении нового экземпляра в базе данных.

мой вариант использования создает новое уведомление (через задачу сельдерея), и как только уведомление сохранено, отправка этого уведомления одному пользователю.

Это звучит так, как будто это возможно (из django-каналы docs)

...решающая часть заключается в том, что вы можете запустить код (и так отправить каналы) в ответ на любое событие - и это включает в себя те, которые вы создавать. Вы можете вызвать на модели сохраняет, на других входящих сообщениях, или из путей кода внутри представлений и форм.

однако, читая документы дальше и играя с django-примеры каналов, Я не вижу, как я могу это сделать. Привязка данных и примеры liveblog демонстрируют отправку в группу, но я не вижу, как просто отправить одному пользователю.

любые предложения были бы очень признательны.

5 ответов


расширение ответа @Flip о создании группы для этого конкретного пользователя.

в вашей функции python в вашей функции ws_connect вы можете добавить этого пользователя в группу a только для них:

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:

мой 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 он будет молча терпеть неудачу.

вам также необходимо убедиться, что вы подключаете только одного пользователя к группе каждого пользователя, иначе несколько пользователей могут получить сообщение, предназначенное только для конкретного пользователя.

посмотрите на примеры каналов django, особенно multichat для реализации маршрутизации создайте соединение websocket на стороне клиента и настройте django_channels.

убедитесь, что вы посмотрите на Django каналы docs.


небольшое обновление, так как группы работают по-разному с каналами 2, чем с каналами 1. Больше нет группового класса, как уже упоминалось здесь.

новый API групп документирован здесь. См. также здесь.

что работает для меня:

# 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
        }))

лучший подход-создать группу для этого конкретного пользователя. Когда ws_connect вы можете добавить этого пользователя в Group("%s" % <user>).add(message.reply_channel)

Примечание: мой url websocket ws://127.0.0.1:8000/<user>


чтобы расширить ответ @luke_aus, если вы работаете с ResourceBindings, вы также можете сделать так, чтобы только пользователи, "владеющие" объектом, получали обновления для них:

как и @luke_aus answer, мы регистрируем пользователя в его собственной группе, где мы можем публиковать действия (update, create) и т. д., которые должны быть видны только этому пользователю:

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)

теперь мы можем привязать эту модель к нашей группе пользователей, и все действия (обновление, создание и т. д.) будут опубликованы только одному пользователю:

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]

на каналы 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

этот конкретный хэш вы можете сохранить в таблице пользователя.