Django-Ninja框架中实现消息推送及应用实例
在现代Web应用开发中,消息推送是一个非常重要的功能,它可以用于实现即时通讯、通知提醒等多种场景。传统的轮询方式已经不能满足低延迟、高并发的需求,而WebSocket协议的出现则为解决这些问题提供了新的方案。在Python Web开发领域,Django框架是众多开发者的选择之一,而Django-Ninja则为Django提供了简洁易用的RESTful API接口设计工具。本文将介绍如何在Django-Ninja框架中结合Django Channels实现消息推送,并通过一个具体的示例来展示整个过程。
## 一、环境准备
首先,确保你有一个基本的Python虚拟环境,并且已经安装了Django、Django REST framework (DRF) 和 Ninja。如果没有安装,可以通过以下命令进行安装:
pip install django djangorestframework ninja
假设我们已经有了一个名为myproject
的Django项目,并且创建了一个名为notifications
的应用。
## 二、安装Django Channels
在我们的项目中安装Django Channels:
pip install channels
## 三、配置Django Channels
编辑myproject/settings.py
文件,添加channels的相关配置:
INSTALLED_APPS = [
# ...
'channels',
'rest_framework',
'ninja',
'notifications',
]
ASGI_APPLICATION = 'myproject.routing.application'
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels.layers.InMemoryChannelLayer',
},
}
这里我们使用了内存层作为channel层,适用于开发阶段。生产环境中可能需要使用更可靠的channel层实现,比如Redis。
## 四、定义路由
在项目根目录下创建一个routing.py
文件,用于定义WebSocket的路由:
from channels.routing import ProtocolTypeRouter, URLRouter
from django.urls import path
from notifications.consumers import NotificationConsumer
application = ProtocolTypeRouter({
"websocket": URLRouter([
path("ws/notifications/", NotificationConsumer.as_asgi()),
]),
})
这里我们定义了一个WebSocket路由,指向notifications
应用中的NotificationConsumer
类。
## 五、编写Consumer
接下来,在notifications
应用中创建一个名为consumers.py
的文件,用于处理WebSocket连接:
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class NotificationConsumer(AsyncWebsocketConsumer):
async def connect(self):
user_id = self.scope['user'].id
self.user_group_name = f'user_{user_id}'
# Join user's group
await self.channel_layer.group_add(
self.user_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
user_id = self.scope['user'].id
self.user_group_name = f'user_{user_id}'
# Leave user's group
await self.channel_layer.group_discard(
self.user_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to user's group
await self.channel_layer.group_send(
self.user_group_name,
{
'type': 'send_notification',
'message': message
}
)
# Receive message from user's group
async def send_notification(self, event):
message = event['message']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message
}))
这个消费者实现了基本的WebSocket连接、断开和接收消息的功能。当接收到消息后,它会将其发送给同一用户组内的所有客户端。
## 六、创建模型
为了存储和管理通知,我们需要定义一个模型。在notifications/models.py
中添加如下内容:
from django.db import models
from django.conf import settings
class Notification(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
message = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.message
## 七、创建视图
在notifications/views.py
中创建一个视图来处理通知的创建和获取:
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Notification
from .serializers import NotificationSerializer
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
class NotificationAPIView(APIView):
def post(self, request):
serializer = NotificationSerializer(data=request.data)
if serializer.is_valid():
notification = serializer.save(user=request.user)
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
f'user_{request.user.id}',
{
'type': 'send_notification',
'message': notification.message
}
)
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
def get(self, request):
notifications = Notification.objects.filter(user=request.user)
serializer = NotificationSerializer(notifications, many=True)
return Response(serializer.data)
这里我们定义了一个视图来处理POST请求(创建通知)和GET请求(获取通知),并在创建通知后通过Django Channels发送消息。
## 八、序列化器
为了将模型转换为JSON格式,我们需要创建一个序列化器。在notifications/serializers.py
中添加如下内容:
from rest_framework import serializers
from .models import Notification
class NotificationSerializer(serializers.ModelSerializer):
class Meta:
model = Notification
fields = ['message']
## 九、URL配置
在notifications/urls.py
中配置URL:
from django.urls import path
from .views import NotificationAPIView
urlpatterns = [
path('notifications/', NotificationAPIView.as_view(), name='notifications'),
]
## 十、集成Ninja
在myproject/urls.py
中集成Ninja,并包含notifications
的URL:
from django.contrib import admin
from django.urls import path, include
from ninja import NinjaAPI
api = NinjaAPI()
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', api.urls),
path('notifications/', include('notifications.urls')),
]
## 十一、前端集成
为了让前端能够与WebSocket通信,你可以使用任何WebSocket库。例如,使用JavaScript原生API:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>消息推送示例</title>
</head>
<body>
<h1>消息推送示例</h1>
<div id="messages"></div>
<input type="text" id="messageInput" placeholder="输入消息...">
<button onclick="sendMessage()">发送</button>
<script>
var socket = new WebSocket(`ws://${window.location.host}/ws/notifications/`);
socket.onopen = function(e) {
console.log("WebSocket连接已打开");
};
socket.onmessage = function(event) {
var data = JSON.parse(event.data);
var messagesDiv = document.getElementById('messages');
messagesDiv.innerHTML += `<p>${data.message}</p>`;
};
function sendMessage() {
var input = document.getElementById("messageInput");
socket.send(JSON.stringify({message: input.value}));
input.value = '';
}
socket.onerror = function(error) {
console.error("WebSocket发生错误: ", error);
};
</script>
</body>
</html>
这段代码会在页面加载时建立WebSocket连接,并尝试发送一条消息。
完整代码示例
myproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'channels',
'rest_framework',
'ninja',
'notifications',
]
ASGI_APPLICATION = 'myproject.routing.application'
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels.layers.InMemoryChannelLayer',
},
}
myproject/routing.py
from channels.routing import ProtocolTypeRouter, URLRouter
from django.urls import path
from notifications.consumers import NotificationConsumer
application = ProtocolTypeRouter({
"websocket": URLRouter([
path("ws/notifications/", NotificationConsumer.as_asgi()),
]),
})
notifications/models.py
from django.db import models
from django.conf import settings
class Notification(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
message = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.message
notifications/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Notification
from .serializers import NotificationSerializer
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
class NotificationAPIView(APIView):
def post(self, request):
serializer = NotificationSerializer(data=request.data)
if serializer.is_valid():
notification = serializer.save(user=request.user)
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
f'user_{request.user.id}',
{
'type': 'send_notification',
'message': notification.message
}
)
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
def get(self, request):
notifications = Notification.objects.filter(user=request.user)
serializer = NotificationSerializer(notifications, many=True)
return Response(serializer.data)
notifications/serializers.py
from rest_framework import serializers
from .models import Notification
class NotificationSerializer(serializers.ModelSerializer):
class Meta:
model = Notification
fields = ['message']
notifications/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class NotificationConsumer(AsyncWebsocketConsumer):
async def connect(self):
user_id = self.scope['user'].id
self.user_group_name = f'user_{user_id}'
# Join user's group
await self.channel_layer.group_add(
self.user_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
user_id = self.scope['user'].id
self.user_group_name = f'user_{user_id}'
# Leave user's group
await self.channel_layer.group_discard(
self.user_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to user's group
await self.channel_layer.group_send(
self.user_group_name,
{
'type': 'send_notification',
'message': message
}
)
# Receive message from user's group
async def send_notification(self, event):
message = event['message']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message
}))
notifications/urls.py
from django.urls import path
from .views import NotificationAPIView
urlpatterns = [
path('notifications/', NotificationAPIView.as_view(), name='notifications'),
]
myproject/urls.py
from django.contrib import admin
from django.urls import path, include
from ninja import NinjaAPI
api = NinjaAPI()
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', api.urls),
path('notifications/', include('notifications.urls')),
]
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>消息推送示例</title>
</head>
<body>
<h1>消息推送示例</h1>
<div id="messages"></div>
<input type="text" id="messageInput" placeholder="输入消息...">
<button onclick="sendMessage()">发送</button>
<script>
var socket = new WebSocket(`ws://${window.location.host}/ws/notifications/`);
socket.onopen = function(e) {
console.log("WebSocket连接已打开");
};
socket.onmessage = function(event) {
var data = JSON.parse(event.data);
var messagesDiv = document.getElementById('messages');
messagesDiv.innerHTML += `<p>${data.message}</p>`;
};
function sendMessage() {
var input = document.getElementById("messageInput");
socket.send(JSON.stringify({message: input.value}));
input.value = '';
}
socket.onerror = function(error) {
console.error("WebSocket发生错误: ", error);
};
</script>
</body>
</html>
## 十二、启动你的应用
现在可以启动你的Django开发服务器,并通过访问ws://localhost:8000/ws/notifications/
来测试WebSocket连接。
python manage.py runserver
以上就是如何在Django-Ninja框架中结合Django Channels实现消息推送,并通过一个具体的示例来展示整个过程。通过这个例子,你可以了解到如何在Django项目中引入WebSocket支持,并利用Django Channels实现简单的多用户实时通信。希望这篇教程能够帮助你在未来开发更加丰富的实时应用。