使用django-channels向单个用户发送消息

时间:2016-09-04 23:10:39

标签: django django-channels

我一直在尝试django-channels,包括阅读文档和玩弄示例。

我希望能够通过将新实例保存到数据库来触发向单个用户发送消息。

我的用例是创建新通知(通过芹菜任务),一旦通知已保存,将此通知发送给单个用户。

这听起来有可能(来自django-channels docs

  

......关键部分是你可以运行代码(等等发送   频道)响应任何事件 - 包括你   创建。您可以触发模型保存,其他传入消息或   来自视图和表单中的代码路径。

然而,进一步阅读文档并使用django-channels examples,我无法理解如何做到这一点。数据绑定和liveblog示例演示了发送到组,但我无法看到如何发送给单个用户。

我们非常感谢任何建议。

6 个答案:

答案 0 :(得分:15)

扩展@ Flip的答案,为该特定用户创建一个组。

在ws_connect函数的python函数中,您可以将该用户添加到一个组中,仅供他们使用:

<强> 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 channels docs

答案 1 :(得分:7)

由于群组与频道2的工作方式不同,因此与频道1的工作方式不同,因此不再有更新。如上所述,here不再有Group类。

新的群组API已记录在案here。另请参阅here

对我有用的是:

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

不要忘记在Consumer中定义一个处理消息类型的方法!

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

答案 2 :(得分:3)

最好的方法是为该特定用户创建组。在ws_connect中,您可以将该用户添加到Group("%s" % <user>).add(message.reply_channel)

  

注意:我的websocket网址为ws://127.0.0.1:8000/<user>

答案 3 :(得分:0)

只是为了扩展@luke_aus的答案,如果您正在使用ResourceBindings,您也可以这样做,只有用户&#34;拥有&#34;对象检索这些更新:

就像@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)

现在我们可以将此模型绑定到我们的用户组,所有操作(更新,创建等)只会发布给这一个用户:

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]

答案 4 :(得分:0)

Channels 2 中,您可以将self.channel_name保存在连接方法的数据库中,该方法是每个用户的特定哈希。 Documentation here

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

的内容

您可以保存在用户表中的特定哈希值。

答案 5 :(得分:-1)

虽然已经很晚了,但是我对频道2有一个直接的解决方案,即使用send而不是group_send

send(self, channel, message)
 |      Send a message onto a (general or specific) channel.

用作-

await self.channel_layer.send(
            self.channel_name,
            {
                'type':'bad_request',
                'user':user.username,
                'message':'Insufficient Amount to Play',
                'status':'400'
            }
        )

处理-

await self.send(text_data=json.dumps({
            'type':event['type'],
            'message': event['message'],
            'user': event['user'],
            'status': event['status']
        }))

谢谢