Loading

在 Django 中跨文件调用已连接的 WebSocket 进行数据传送

在 Django 中跨文件调用已连接的 WebSocket 进行数据传送

在 Django 项目中,如果你想在其他文件中向已连接的 WebSocket 客户端发送数据,有几种实现方式。以下是基于 Django Channels 的解决方案:

方法一:使用 Channel Layers(推荐)

这是最标准的方式,适用于生产环境,支持分布式部署。

1. 配置 Channel Layers

settings.py 中:

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],  # Redis 地址
        },
    },
}

安装所需包:

pip install channels-redis

2. 修改消费者 (consumers.py)

from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
import json

class MyConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = "notification"  # 组名
        await self.channel_layer.group_add(
            self.room_name,
            self.channel_name
        )
        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.room_name,
            self.channel_name
        )

    # 接收从组发来的消息
    async def send_notification(self, event):
        message = event['message']
        await self.send(text_data=json.dumps({
            'message': message
        }))

3. 在其他文件中发送消息

在任何 Python 文件中(如 views.py、tasks.py 等):

from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync

def send_ws_message(message):
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        "notification",  # 组名,与消费者中一致
        {
            "type": "send.notification",  # 对应消费者中的方法名(点换成下划线)
            "message": message
        }
    )

方法二:使用全局变量(仅适用于开发/单进程)

如果只是简单项目且单进程运行,可以使用全局变量存储连接(不推荐生产环境使用)。

1. 创建全局存储文件 (websocket_connections.py)

active_connections = set()

def add_connection(connection):
    active_connections.add(connection)

def remove_connection(connection):
    active_connections.discard(connection)

async def broadcast_message(message):
    for connection in active_connections:
        await connection.send(text_data=message)

2. 修改消费者

from .websocket_connections import add_connection, remove_connection, broadcast_message

class MyConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        await self.accept()
        add_connection(self)

    async def disconnect(self, close_code):
        remove_connection(self)

3. 在其他文件中发送消息

from .websocket_connections import broadcast_message
import asyncio

async def send_data():
    await broadcast_message("Hello from another file!")

# 如果在同步上下文中调用
asyncio.run(send_data())

方法三:使用数据库信号或 Celery 任务

如果数据变化来自模型,可以使用 Django 信号:

from django.db.models.signals import post_save
from django.dispatch import receiver
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
from .models import MyModel

@receiver(post_save, sender=MyModel)
def model_updated(sender, instance, **kwargs):
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        "updates",
        {
            "type": "send.update",
            "message": f"Model {instance.id} updated"
        }
    )

最佳实践建议

  1. 生产环境:使用 Channel Layers + Redis
  2. 开发环境:可以使用内存 Channel Layer
    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "channels.layers.InMemoryChannelLayer"
        }
    }
    
  3. 性能考虑:对于高频消息,考虑批处理或限制发送频率
  4. 安全性:确保只有授权用户才能接收消息

完整示例:从视图发送 WebSocket 消息

# views.py
from django.http import JsonResponse
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync

def send_message_view(request):
    message = request.GET.get('message', 'Hello')
    
    # 发送到 WebSocket
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        "notification",
        {
            "type": "send.notification",
            "message": message
        }
    )
    
    return JsonResponse({"status": "ok"})
posted @ 2025-05-29 16:50  一只大学生  阅读(39)  评论(0)    收藏  举报