【问题标题】:Sending a message to a single user using django-channels使用 django-channels 向单个用户发送消息
【发布时间】:2017-01-12 07:57:17
【问题描述】:

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

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

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

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

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

但是,进一步阅读文档并使用django-channels examples,我不知道该怎么做。数据绑定和 liveblog 示例演示了发送到组,但我看不到如何只发送给单个用户。

【问题讨论】:

标签: django django-channels


【解决方案1】:

更新很少,因为组在通道 2 上的工作方式与在通道 1 上的工作方式不同。不再有组类,如 here 所述。

新的组 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
        }))

【讨论】:

  • 您是如何向特定用户发送消息的?
  • 您可以为每个人创建一个频道。
  • @user42488 但对于单个用户,它会创建自己的频道名称。
  • 出于好奇:您为什么使用'{}'.format(group_name) 而不仅仅是group_name 或者str(group_name)
  • 嗨@rolando,组名的转换取决于组名的传入格式。 str() 或 '{}'.format 是一个偏好问题。
【解决方案2】:

扩展@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 代码向该用户发送消息:

我的观点.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

【讨论】:

  • 这是预期的方法,还是只是一种解决方法。
  • 在没有 Group 的消费者之外,我看不到任何方式。您必须在消费者中才能获得 message.reply_chanel。在消费者之外,您必须创建一个组来访问用户。在此处查看相关文档:channels.readthedocs.io/en/stable/concepts.html#channel-types。还可以查看此处提供的 Multichat 和 Livebloe 示例:github.com/andrewgodwin/channels-examples
  • 也有同样的问题。为此创建一个组并不是它的真正意图,我想我们没有其他选择
  • 在实施这个方案之前,我最大的担心是:Groups 为空时是否会自行删除?还是每个用户都会创建一个组,即使他们再也不会回来,也会继续存在?
  • 如果恶意用户使用控制台添加到一组用户并且您正在发送私人数据怎么办?
【解决方案3】:

Channels 2 中,您可以将 self.channel_name 保存在 db on connect 方法中,该方法是每个用户的特定哈希。 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

这个特定的哈希可以保存在用户的表中。

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

    • 谢谢。所以你是说为我的用例为每个用户创建一个组?
    • 我想知道的是同样的事情,这将如何扩展。
    【解决方案5】:

    只是为了扩展@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)
    

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

    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]
    

    【讨论】:

      【解决方案6】:

      虽然已经晚了,但我有一个直接解决渠道 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']
              }))
      

      谢谢

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-08-06
        • 2017-06-09
        • 2023-03-17
        • 2018-07-29
        • 1970-01-01
        • 2021-03-22
        • 2020-05-13
        • 2018-07-11
        相关资源
        最近更新 更多