Django-channels:ChatConsumer 仅向一个用户发送消息,而不是向两个用户发送消息

2024-03-04

我正在使用 django-channels 和 redis 在 django 和 Angular 中实现聊天应用程序。

套接字已连接并正常工作,但我面临的问题是,当两个用户在线并使用相同的线程 url 连接同一个聊天室时,它会连接,但任何用户发送的消息仅发送给最近连接套接字的用户并且它们仅发送给该用户两次。

在 django 中我做了以下配置:

设置/base.py

....
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.sites',

    'channels',
    'chats'
]

ASGI_APPLICATION = "influnite.routing.application"

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("localhost", 6379)],
            # "hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')]
        },
    },
}
....

路由.py

from django.conf.urls import url
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from channels.security.websocket import AllowedHostsOriginValidator, OriginValidator

from chats.consumers import ChatConsumer

application = ProtocolTypeRouter({
    'websocket': AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                [
                    url(r"^messages/(?P<thread_id>[\w.+]+)/", ChatConsumer())
                ]
            )
        )
    )
})

我创建了三个模型,即Thread, ThreadMember & ChatMessage.

聊天/models.py

from django.db import models
from django.db.models import Q
from django.utils import timezone
from django.contrib.auth.models import User

from base.models import BaseModel

# Create your models here.
MESSAGE_TYPE = [
    ('text', 'Text'),
    ('audio', 'Audio'),
    ('img', 'Image'),
    ('doc', 'Document'),
    ('link', 'Link')
]

THREAD_TYPE = [
    ('individual', 'Individual'),
    ('group', 'Group')
]

class Thread(BaseModel):
    name = models.CharField(max_length=20, null=True, blank=True)
    timestamp = models.DateTimeField(auto_now_add=True)
    thread_type = models.CharField(max_length=20, choices=THREAD_TYPE, default='individual')

    class Meta:
        db_table = 'in_thread'
        verbose_name = 'threads'
        verbose_name_plural = 'thread'
        ordering = ['-update_date']

class ThreadMember(BaseModel):
    thread = models.ForeignKey(Thread, on_delete=models.CASCADE, related_name='thread_member')
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='thread_member_user')
    is_grp_admin = models.BooleanField(default=False)

    def __str__(self):
        return f'{self.thread.name} > {self.user}'

    class Meta:
        db_table = 'in_thread_member'
        verbose_name = 'thread members'
        verbose_name_plural = 'thread member'

class ChatMessage(BaseModel):
    thread = models.ForeignKey(Thread, on_delete=models.CASCADE, related_name='msg_thread')
    sender = models.ForeignKey(ThreadMember, on_delete=models.CASCADE, related_name='msg_sender')
    message = models.TextField(null=True, blank=True)
    sent_at = models.DateTimeField(default=timezone.now())
    read_receipt = models.BooleanField(default=False)
    msg_type = models.CharField(max_length=20, choices=MESSAGE_TYPE, default='text')

    def __str__(self):
        return f'{self.sender} > {self.message}'

    class Meta:
        db_table = 'in_chat_message'
        verbose_name = 'chat message'
        verbose_name_plural = 'chat messages'
        ordering = ['sent_at']

下面是consumers.py包含 ChatConsumer 类的文件。

聊天/consumer.py

from django.contrib.auth.models import User

import asyncio, json
from channels.consumer import AsyncConsumer
from channels.db import database_sync_to_async

from .models import Thread, ThreadMember, ChatMessage
from .serializers import ChatMessageSerializer

class ChatConsumer(AsyncConsumer):
    async def websocket_connect(self, event):
        print("connected", event)
        try:
            kwargs = self.scope['url_route']['kwargs']

            thread_id = kwargs.get('thread_id', False)
            if thread_id:
                thread = await self.get_thread(thread_id)
                if thread:
                    self.chat_room = f'thread_{thread_id}'
                    await self.channel_layer.group_add(
                        self.chat_room,
                        self.channel_name
                    )
                    await self.send({
                        "type": "websocket.accept"
                    })
            else:
                await self.send({
                    "type": "websocket.close"
                })
        except Exception as e:
            print("Error in websocket connection!")
            print(e)

    async def websocket_receive(self, event):
        print("receive", event)
        try:
            kwargs = self.scope['url_route']['kwargs']
            
            thread_id = kwargs.get('thread_id', False)
            thread = await self.get_thread(thread_id)

            response = event.get('text', False)
            response = json.loads(response)
            message = response.get('message', False)

            if message:
                data, message_saved = await self.save_message(
                    message, response.get('sender'), thread)

                if message_saved:
                    text = json.dumps(response)

                    if thread:
                        await self.channel_layer.group_send(
                            self.chat_room,
                            {
                                "type": "chat_message",
                                "text": text
                            }
                        )
        except Exception as e:
            print("Error in websocket receive!")
            print(e)
    
    async def websocket_disconnect(self, event):
        print("disconnected", event)

    async def chat_message(self, event):
        """sends the actual message"""
        try:
            await self.send({
                "type": "websocket.send",
                "text": event['text']
            })
        except Exception as e:
            print("Error sending messages!")
            print(e)

    @database_sync_to_async
    def get_thread(self, thread_id):
        return Thread.objects.get(id=thread_id)
    
    @database_sync_to_async
    def save_message(self, message, sender, thread):
        try:
            sender = ThreadMember.objects.get(
                thread=thread.id,
                user=User.objects.get(id=sender))
            chat = ChatMessage.objects.create(
                thread=thread,
                sender=sender,
                message=message
            )
            chat.save()
            thread.save()
            return ChatMessageSerializer(chat).data, True
        except Exception as e:
            print("Error saving chat!")
            print(e)
            return False

当我运行 redis 服务器时,我得到以下信息。

C:\Users\rh>cd C:\Program Files\Redis
C:\Program Files\Redis>redis-server redis.windows.conf
                _._
           _.-``__ ''-._
      _.-``    `.  `_.  ''-._           Redis 5.0.10 (1c047b68/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 9628
  `-._    `-._  `-./  _.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |           http://redis.io
  `-._    `-._`-.__.-'_.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |
  `-._    `-._`-.__.-'_.-'    _.-'
      `-._    `-.__.-'    _.-'
          `-._        _.-'
              `-.__.-'

[9628] 11 Dec 12:38:17.011 # Server initialized
[9628] 11 Dec 12:38:17.011 * DB loaded from disk: 0.000 seconds
[9628] 11 Dec 12:38:17.011 * Ready to accept connections

我不知道我在这里做错了什么,我感谢一些帮助。如果需要更多信息,我会改进我的问题。

提前致谢!


就我而言,我需要更新到channels至少 3.0.1(由于https://github.com/django/channels/issues/1550 https://github.com/django/channels/issues/1550).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Django-channels:ChatConsumer 仅向一个用户发送消息,而不是向两个用户发送消息 的相关文章

  • 从字典的元素创建 Pandas 数据框

    我正在尝试从字典创建一个 pandas 数据框 字典设置为 nvalues y1 1 2 3 4 y2 5 6 7 8 y3 a b c d 我希望数据框仅包含 y1 and y2 到目前为止我可以使用 df pd DataFrame fr
  • 如何在python中附加两个字节?

    说你有b x04 and b x00 你如何将它们组合起来b x0400 使用Python 3 gt gt gt a b x04 gt gt gt b b x00 gt gt gt a b b x04 x00
  • Python re无限执行

    我正在尝试执行这段代码 import re pattern r w w s re compiled re compile pattern results re compiled search COPRO HORIZON 2000 HOR p
  • 指示电子邮件的类型

    我有以下自动化程序 它将电子邮件发送给我自己 并添加了特定的链接 import win32com client as win32 import easygui import tkinter as to from tkinter import
  • 如何使用 Python boto3 获取 redshift 中的列名称

    我想使用 python boto3 获取 redshift 中的列名称 创建Redshift集群 将数据插入其中 配置的机密管理器 配置 SageMaker 笔记本 打开Jupyter Notebook写入以下代码 import boto3
  • numpy:高效执行数组的复杂重塑

    我正在将供应商提供的大型二进制数组读入 2D numpy 数组 tempfid M N load data data numpy fromfile file dirname fid dtype numpy dtype i4 convert
  • 将 pandas 剪切操作转换为常规字符串

    我明白了 pandas cut 操作的输出 0 0 20 1 0 20 2 0 20 3 0 20 4 0 20 5 0 20 6 0 20 7 0 20 8 0 20 9 0 20 如何将 0 20 转换为 0 20 我正在这样做 str
  • OpenCV 跟踪器:模型未在函数 init 中初始化

    在视频的第一帧 我运行一个对象检测器 它返回对象的边界框 如下所示
  • python 中的 h2o 框架子集

    如何在 python 中对 h2o 框架进行子集化 如果 x 是一个 df 并且 Origin 是一个变量 那么在 pandas 中我们通常可以通过以下方式进行子集化 x x Origin AAF 但使用 h2o 框架会出现以下错误 H2O
  • 在Python中读取tiff标签

    我正在尝试用 Python 读取 tiff 文件的标签 该文件是 RGB 的uint16每个通道的值 我目前正在使用tifffile import tifffile img tifffile imread file tif 然而 img是一
  • 在 Mac OS X 上安装 libxml2 时出现问题

    我正在尝试在我的 Mac 操作系统 10 6 4 上安装 libxml2 我实际上正在尝试在 Python 中运行 Scrapy 脚本 这需要我安装 Twisted Zope 现在还需要安装 libxml2 我已经下载了最新版本 2 7 7
  • 使 Django 内置 send_mail 函数默认使用 html

    我想替换内置发送邮件功能 仅适用于纯文本电子邮件 用我自己的智能发送邮件函数 自动生成 html 和纯文本版本 一切都按我自己的电子邮件的预期进行 在我自己的应用程序中定义 我可以在views py中以这种方式做到这一点 from djan
  • 使用seaborn绘制简单线图

    我正在尝试使用seaborn python 绘制ROC曲线 对于 matplotlib 我只需使用该函数plot plt plot one minus specificity sensitivity bs where one minus s
  • pygame:使用 sprite.RenderPlain 绘制精灵组的顺序

    我有一个精灵组 需要按一定的顺序绘制 以便其精灵按应有的方式重叠 然而 即使使用运算符模块函数 sorted self sprites key attrgetter y x 对组进行排序 顺序也是错误的 我该如何解决这个问题 直截了当地说
  • 为正则表达式编写解析器

    即使经过多年的编程 我很羞愧地说我从未真正完全掌握正则表达式 一般来说 当问题需要正则表达式时 我通常可以 在一堆引用语法之后 想出一个合适的正则表达式 但我发现自己越来越频繁地使用这种技术 所以 自学并理解正则表达式properly 我决
  • 无法在 PyCharm 版本 9.3.3 中安装 NumPy。 Python版本3.8.2

    在 PyCharm 中安装 NumPy 时出错 尝试安装 Microsoft Visual C 14 0 还是行不通 NumPy 正在通过命令安装pip3 install numpy在 cmd 终端中 但是当尝试将其安装在 PyCharm
  • 跨应用程序使用 Django 模型?

    因此 在我的 Django 项目中 我有几个不同的应用程序 每个应用程序都有自己的模型 视图 模板等 让这些应用程序进行通信的好方法 Django 方式 是什么 一个具体的例子是一个会议应用程序 它有一个会议模型 我有一个家庭应用程序 我想
  • 在 numpy 中连接维度

    我有x 1 2 3 4 5 6 7 8 9 10 11 12 shape 2 2 3 I want 1 2 3 4 5 6 7 8 9 10 11 12 shape 2 6 也就是说 我想连接中间维度的所有项目 在这种特殊情况下我可以得到这
  • PyQt5:如何使QThread返回数据到主线程

    I am a PyQt 5 4 1 1初学者 我的Python是3 4 3 这是我尝试遵循的many https mayaposch wordpress com 2011 11 01 how to really truly use qthr
  • django jet 中的自定义徽标

    我目前正在尝试对 django 管理面板的皮肤进行一些定制 以使其更符合我们的品牌 目前我们使用 django jet 来美化管理面板 django jet 可以自定义 css html 吗 所有评论都说我应该更改一些 html 文件 但我

随机推荐

  • iTextSharp .NET PDF - 无法更改 PDF 生成器

    我正在使用 iTextSharp 产品更改 PDF 属性 如下所示 我根本无法更改 PDF Producer 属性 请大家指教 我哪里错了 代码行info 制片人 我的制片人 没有按应有的方式工作 string sourcePath tbP
  • 如何使用 MATLAB 以定时间隔采集图像?

    我是 MATLAB 初学者 我想知道如何从相机中以 5 秒的间隔采集并保存 20 张图像 非常感谢 首先构造一个视频输入接口 vid videoinput winvideo 1 RGB24 400x300 您需要调整网络摄像头的最后一位 要
  • SQL Server:内联表值 UDF 与内联视图

    我正在使用一个医疗记录系统 该系统将数据存储在类似于电子表格的结构中 列标题中的日期 时间 每行第一列中的测量值 例如医生姓名 Rh 血型 以及一个值在相交的单元格中 基于此构造的报告通常需要显示 10 个或更多这些度量 出于报告目的 数据
  • 循环遍历阵列 MIPS 组件

    我正在开发一个循环遍历 10 个数字的数组的程序 前 9 个元素的值大于 0 第 10 个元素的值为 0 遇到 0 时循环应中断 i 0 while A i 0 A i A i 1 i 我知道如果寄存器的值等于 0 我可以使用 beq 来中
  • WCF 查询拦截器:此 MSDN 示例是否存在安全风险?

    如果你看this http msdn microsoft com en us library dd744837 aspxMSDN 文档中有一个示例 代码如下 Define a change interceptor for the Produ
  • 给出一个通用枚举类型作为模板参数

    简而言之 有什么方法可以喂养General模板化类仅代表一个enum类型 就像是 template
  • Bootstrap 3 删除内联输入之间的空间

    我正在做一个简单的内联表单 如何调整每个输入表单之间的空白 我想让输入基本上接触 div class row div
  • 如何导入带有回调函数的外部JS?

    我正在使用 Google API 根据他们的链接我必须将以下脚本放入 HTML 文件中 client js加载成功后正在加载自定义回调函数 function callback var ROOT https your app id appsp
  • SyntaxError:IncomingMessage 处 JSON.parse () 处的 JSON 输入意外结束。

    我正在尝试制作一个天气应用程序 并且正在使用天气 API 来获取信息 但是当我尝试解析 JSON 数据时会出现此错误 SyntaxError Unexpected end of JSON input at JSON parse
  • java中Iterator()的时间复杂度

    我是 Java 新手 我有一个关于java iterator 的时间复杂度的问题 Set
  • pandas 中的 read_table 和 read_csv 有区别吗?

    我已经对其进行了测试 并检查了文档 没有发现明显的差异 无论哪种方式 我都想问以防万一 您是否认为 read csv 应该仅用于 csv 即使它适用于其他类型 而 read table 有什么作用 它们存在时是否相同 您可以使用其中之一来处
  • 使用 Google+ 环聊 API

    如果有人添加 电子邮件受保护 cdn cgi l email protection到他的 Google Hangout 联系人并向其发送一条 Google Hangout Chat 消息 我想从我的服务器设置自动回复他的聊天 有没有办法做到
  • C# 遍历二维数组

    for int k 0 k lt odds GetLength 1 k 上面的代码行应该迭代 Double 类型的二维数组 但不断抛出以下异常 索引超出范围异常 有哪位好心人能解释一下原因并提供解决方案吗 非常感谢 您正在将无效索引传递给G
  • 是否可以为条形图中的每个类别自定义标签?

    最近 我收到一个要求 需要创建一个显示每个项目数据的条形图 这是一个例子 如您所见 Category是项目的名称 并且Series是该项目中不同类型的数据 但是 由于系统不保证项目名称的唯一性 将其用作类别可能会导致问题 并且我将无法使用项
  • CakePHP Cookie 被打乱 - Suhosin 相关

    由于某种原因 在设置后 我无法在任何页面上读取 CakePHP 应用程序中的任何 cookie 唯一返回的是乱码文本 我的代码很简单 this gt Cookie gt write Region test reg this gt Cooki
  • Linux 系统调用的内部结构

    当线程通过引发中断 80 进行系统调用时会发生什么 详细 Linux 对线程的堆栈和其他状态做了什么工作 对处理器进行了哪些更改才能将其置于内核模式 运行中断处理程序后 控制权如何恢复到调用进程 如果系统调用无法快速完成怎么办 例如从磁盘读
  • 有没有办法让 SQLAlchemy 不在 BEGIN 和 COMMIT 中包装 SQL 写入?

    我在 Pylons 1 0 框架上使用 SQLAlchemy 0 6 4 我已经尝试了将 autoflush 和 autocommit 设置为 True 和 False 的所有排列 但我发现 SQLAlchemy 想要包装所有 SQL 会话
  • R 包不存在

    我收到了可怕的包裹R不存在 它要了我的命 代码很好 我在市场上有运行该代码的应用程序 现在效果很简单webview应用程序 的 0 R string app name我的有错吗main xml and string xml很好 我很困惑 我
  • Libgdx - 支持 OpenGL 4+

    是否可以仅使用 Libgdx 进行桌面开发并从 Libgdx 抽象访问较新版本的 Opengl 我的意思是 如果你想开发移动应用程序 你将必须使用 Opengl ES 的某个版本 它不支持经典 Opengl 那样的很多功能 例如绘制线框等
  • Django-channels:ChatConsumer 仅向一个用户发送消息,而不是向两个用户发送消息

    我正在使用 django channels 和 redis 在 django 和 Angular 中实现聊天应用程序 套接字已连接并正常工作 但我面临的问题是 当两个用户在线并使用相同的线程 url 连接同一个聊天室时 它会连接 但任何用户