您现在的位置是:首页 > 学无止境 > Python 网站首页学无止境

Django Channels 实现点对点实时聊天(方法二)

在前面一篇博客中,提到了一种完全依赖websocket来传递消息的实时聊天方法。在这里,我们再介绍另一种方法,也是依赖了websocket,但是在这里websocket起到的作用和消息推送一模一样。具体方法如下


开始方法与上一篇文章类似,新建一个consumers.py文件,

class PushConsumer(AsyncWebsocketConsumer):
    # chats = dict()

    async def connect(self):

        await self.channel_layer.group_add(self.group_name, self.channel_name)

        # 创建连接时调用
        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.group_name,
            self.channel_name
        )
        # PushConsumer.chats[self.group_name].remove(self)
        # print(PushConsumer.chats)

    async def push_message(self, event):
        print(event)
        await self.send(text_data=json.dumps({
            "data": event['event']
        }))

大家看一下可以发现,其实代码和上一篇文章中推送功能的PushConsumer代码一致。然后再在models.py文件里面新增一个Message模块,借助django的signals模块,每当有一条消息新建时,就会自动把这条消息的id实时推送给消息发送者和消息接收者。

# 聊天记录
class Messages(models.Model):
    # 信息发送者
    sender = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='message_sender')
    # 信息接收者
    receiver = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='message_receiver')
    # 信息内容
    body = models.CharField(max_length=1024, verbose_name='message_body')
    # 发送时间
    created = models.DateTimeField(auto_now_add=True)
    # 状态
    status = models.BooleanField(default=False)

    def __str__(self):
        return self.body

    class Meta:
        verbose_name = 'message'
        verbose_name_plural = 'messages'
        ordering = ('-created',)


# 消息推送
def push(username, event):
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        username,
        {
            "type": "push.message",
            "event": event
        }
    )


@receiver(post_save, sender=Messages)
def post_application_notice(sender, instance=None, created=False, **kwargs):
    entity = instance
    if created:
        push(entity.sender.username, {'type': 5, 'text': entity.id})
        push(entity.receiver.username, {'type': 5, 'text': entity.id})

每一位用户都连接到了一个以自己username作为识别的websocket route,具体route形式为 url(r'^push/(?P<username>[0-9a-z]+)/$', consumers.PushConsumer) 这样每当用户接收到type为5的websocket消息时,就明白这是一条聊天信息,然后就可以请求一下消息详情的api,获取这条消息。

# 获取某一条消息
class MessageDetailView(APIView):
    permission_classes = (IsAuthenticated,)
    authentication_classes = (MyAuthentication,)
    serializer_class = MessageDetailSerializer

    def get(self, request, pk):
        message = Messages.objects.filter(Q(receiver=request.user) | Q(sender=request.user))
        try:
            message = message.get(id=pk)
            # print(MessageDetailSerializer(message))
        except Messages.DoesNotExist:
            raise NotFound('60001Not found the message.')
        else:
            msg = Response({
                'error': '0',
                'data': MessageDetailSerializer(message).data,
            }, HTTP_200_OK)
            return msg

就这样,一种点对点的实时聊天就完成了。


完善这功能还有很多方面,例如在消息建立时推送给接收者的不仅仅是消息的id,还有发送者;在进入一个聊天界面时可以获取最近的15条聊天记录;具体的完善方式在这里就不一一列举的,因为并不是很难,并且也不涉及到websocket,各位可以自行添加或修改。


版权声明:本文为博主原创文章,转载时请注明来源。https://blog.thinker.ink/passage/16/

 

文章评论

Top