使用Django的实时聊天应用程序
聊天室已经是创建实时和现场项目的最基本步骤。我们将创建的聊天页面将是一个简单的HTML模板,其中有一个简单的h1文本,上面有当前用户的名字和一个链接,用于注销刚刚登录的用户。你可能需要对这一行进行注释,直到我们为此创建auth系统。这确保了当两个用户在聊天时,一个人可以注销而不影响另一个用户。另一个人仍然会登录,他可以发送和接收信息。
Django通道和异步编程的介绍
Channels是一个Python项目,它的创建是为了将Django的能力扩展到新的水平。我们在标准的Django中工作,它不支持异步和通道以及通过WebSockets的连接来创建实时应用程序。Channels将Django的能力扩展到HTTP之外,使其能够与WebSockets、聊天协议、物联网协议等一起工作。它建立在ASGI的支持上,ASGI代表异步服务器网关接口。ASGI是WSGI的继承者,它提供了一个异步和python之间的接口。Channels提供了ASGI的功能,通过对WSGI的扩展,它用WSGI提供了ASGI支持。Channels还将事件驱动架构与通道层捆绑在一起,这个系统允许你轻松地在进程之间进行通信,并将你的项目分成不同的进程。
创建聊天应用程序的步骤
第1步:安装和设置Django
步骤2:创建你的虚拟环境。
步骤3:然后创建一个名为ChatApp的Django项目。为了创建该项目,在终端写下命令。
django-admin startproject ChatApp
第四步:创建项目后,文件夹结构应该是这样的,然后打开你最喜欢的IDE进一步工作。
第5步:安装django-channels,以便与聊天应用程序一起工作。这将在你的环境中安装通道。
python -m pip install -U channels
第6步:在安装完频道后,将频道添加到你已安装的应用程序中。这将让Django知道项目中已经引入了频道,我们可以进一步工作。
INSTALLED_APPS = [
'chat.apps.ChatConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# add django channels
'channels' ,
]
第7步:将ASGI应用程序设置为你项目中的默认ASGI文件。现在运行服务器,你会注意到ASGI服务器将在Django服务器上进行,现在它将支持ASGI。
ASGI_APPLICATION = 'ChatApp.asgi.application'
要运行该服务器,在终端写下以下命令。
python manage.py runserver
第8步:创建一个新的应用程序,将拥有所有的聊天功能。为了创建一个应用程序,在终端写一个命令。
python manage.py startapp chat
并在settings.py中把你的应用程序添加到已安装的应用程序中。
第9步:在你的聊天应用程序中创建一些文件
- chat/urls.py。这将把Django应用程序路由到应用程序中的不同视图。
- 创建一个模板文件夹。在你的应用程序内,在模板/聊天室内创建两个文件,命名为chat.Page.html,和LoginPage.html。
- routing.py。这将把WebSocket连接路由给消费者。
- consumers.py。这是所有异步功能将发生的文件
将此代码粘贴到您的ChatApp/urls.py中。这将把你引向你的聊天应用程序。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path("", include("chat.urls")),
]
将这段代码粘贴到你的chat/urls.py中。这将把你引向视图。
from django.urls import path, include
from chat import views as chat_views
from django.contrib.auth.views import LoginView, LogoutView
urlpatterns = [
path("", chat_views.chatPage, name="chat-page"),
# login-section
path("auth/login/", LoginView.as_view
(template_name="chat/LoginPage.html"), name="login-user"),
path("auth/logout/", LogoutView.as_view(), name="logout-user"),
]
将这段代码粘贴到您的views.py中。这将把你的视图引向chatPage.html,该视图已在聊天应用程序的模板文件夹中创建。
from django.shortcuts import render, redirect
def chatPage(request, *args, **kwargs):
if not request.user.is_authenticated:
return redirect("login-user")
context = {}
return render(request, "chat/chatPage.html", context)
这个URL是Django的格式,这是Django的语法,用来映射到一个URL。我们将创建一个名为 “logout-user “的URL,然后Django将这个URL名称映射到模板中的URL。Django提供了一些pythonic语法来处理控制语句。这里我们在HTML中提供了{% if request.user.is_authenticated %}行,这是Django提供的,它确保如果有任何用户登录,那么只显示注销链接。
- templates/chat/chatPage.html
<!DOCTYPE html>
<html>
<body>
<center><h1>Hello , Welcome to my chat site ! {{request.user}}</h1></center>
<br>
{% if request.user.is_authenticated %}
<center> Logout the chat Page <a href = "{% url 'logout-user' %}">Logout</a></center>
{% endif %}
<div
class="chat__item__container"
id="id_chat_item_container"
style="font-size: 20px"
>
<br />
<input type="text" id="id_message_send_input" />
<button type="submit" id="id_message_send_button">Send Message</button>
<br />
<br />
</div>
<script>
const chatSocket = new WebSocket("ws://" + window.location.host + "/");
chatSocket.onopen = function (e) {
console.log("The connection was setup successfully !");
};
chatSocket.onclose = function (e) {
console.log("Something unexpected happened !");
};
document.querySelector("#id_message_send_input").focus();
document.querySelector("#id_message_send_input").onkeyup = function (e) {
if (e.keyCode == 13) {
document.querySelector("#id_message_send_button").click();
}
};
document.querySelector("#id_message_send_button").onclick = function (e) {
var messageInput = document.querySelector(
"#id_message_send_input"
).value;
chatSocket.send(JSON.stringify({ message: messageInput, username : "{{request.user.username}}"}));
};
chatSocket.onmessage = function (e) {
const data = JSON.parse(e.data);
var div = document.createElement("div");
div.innerHTML = data.username + " : " + data.message;
document.querySelector("#id_message_send_input").value = "";
document.querySelector("#id_chat_item_container").appendChild(div);
};
</script>
</body>
</html>
{{request.user.userrname}}告诉当前登录的用户的用户名。如果用户已经登录,就会给出其用户名;如果没有登录,就不会打印任何东西。聊天页面现在看起来是这样的,因为没有当前登录的用户,{{request.user.username}}没有打印出任何东西。
- templates/chat/LoginPage.html
<!DOCTYPE html>
<html>
<body>
<form method ="post">
{% csrf_token %}
{{form.as_p}}
<br>
<button type = "submit">Login</button>
</form>
</body>
</html>
实现WebSockets、异步和Django通道
到现在为止,我们已经建立了我们的标准Django项目。在实现了Django应用程序之后。现在是创建ASGI应用程序的时候了。
第10步:首先迁移你的数据库。
python manage.py makemigrations
python manage.py migrate
第11步:打开routing.py,为ChatConsumer创建一个路由(我们将在下一步创建)。现在我们在项目中有两种类型的路由。首先是urls.py,它是Django本地的URL路由,另一个是Django的ASGI支持的WebSockets。
chat/routing.py
from django.urls import path , include
from chat.consumers import ChatConsumer
# Here, "" is routing to the URL ChatConsumer which
# will handle the chat functionality.
websocket_urlpatterns = [
path("" , ChatConsumer.as_asgi()) ,
]
第12步 。Open consumers.py将处理事件,如onmessage事件,onopen事件等,我们将在chatPage.html中看到这些事件,我们已经创建了socket连接。
代码解释:
- class ChatConsumer(AsyncWebsocketConsumer)。这里我们要创建一个名为ChatConsumer的类,它继承自AsyncWebsocketConsumer,用于创建、销毁WebSockets并做一些其他事情。而在这里,我们正在创建ChatSocket以达到所需的目的。
- async def connect(self):这个函数在已经创建的websocket实例上工作,当连接被打开或创建时,它连接并接受该连接。它为聊天室创建一个组名,并将该组添加到频道层组。
- async def disconnect():这只是将该实例从组中移除。
- async def receive():当我们从WebSocket发送数据时,这个函数就会被触发(工作的事件是:发送),这个函数接收文本数据,它已经被转换成JSON格式(因为它适合于javascript),在收到文本数据后,它需要被传播到其他在组中活动的实例。我们检索消息参数,它持有消息和用户名参数,这些参数是由socket通过HTML或js发送。这个收到的消息将通过channel_layer.group_send()方法传播给其他实例,该方法的第一个参数是roomGroupName,即这个实例属于哪个组,数据需要在哪里发送。然后第二个参数是字典,它定义了处理数据发送的函数(”type”: “sendMessage”),同时字典中还有保存消息数据的变量消息。
- async def sendMessage(self, event):这个函数接收要发送数据的实例和事件,基本上事件保存了通过receive()函数的group_send()方法发送的数据。然后,它将消息和用户名参数发送给该组中所有活跃的实例。并以JSON格式转储,以便js能够理解这个符号。JSON是一种格式(Javascript对象记号)。
chat/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.roomGroupName = "group_chat_gfg"
await self.channel_layer.group_add(
self.roomGroupName ,
self.channel_name
)
await self.accept()
async def disconnect(self , close_code):
await self.channel_layer.group_discard(
self.roomGroupName ,
self.channel_layer
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json["message"]
username = text_data_json["username"]
await self.channel_layer.group_send(
self.roomGroupName,{
"type" : "sendMessage" ,
"message" : message ,
"username" : username ,
})
async def sendMessage(self , event) :
message = event["message"]
username = event["username"]
await self.send(text_data = json.dumps({"message":message ,"username":username}))
我们在routing.py中映射的ChatConsumer与我们刚刚创建的consumer.py是一样的。这些脚本是异步模式的,因此我们在这里使用async和await工作。
第13步:在你的asgi.py中写下以下代码,使其与套接字一起工作并创建路由。
ChatApp/asgi.py
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ChatApp.settings')
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter , URLRouter
from chat import routing
application = ProtocolTypeRouter(
{
"http" : get_asgi_application() ,
"websocket" : AuthMiddlewareStack(
URLRouter(
routing.websocket_urlpatterns
)
)
}
)
我们通常使用wsgi.py工作,它在标准Django中没有任何异步支持。但在这里我们使用的是异步通道。所以我们必须以不同于URLs的方式来定义路由。对于HTTP,我们定义使用我们已经使用过的正常应用,现在我们引入了另一个协议,那就是ws(WebSocket),你必须对其进行路由。ProtocolTypeRouter为应用程序中使用的不同类型的协议创建路由。AuthMiddlewareStack负责验证路由和实例,URLRouter负责路由ws(WebSocket连接)。WebSockets的协议被称为 “ws”。对于不同的请求,我们使用HTTP。
这里,路由器将WebSocket URL路由到聊天应用程序中的一个变量,即 “websocket_urlpatterns”,该变量持有WebSocket连接的路由。
第14步:这段代码定义了通道层,我们将在其中工作并分享数据。对于部署和生产层面,不要使用InMemoryChannelLayer,因为有很大的机会让你的数据泄漏。这对生产来说是不利的。对于生产来说,使用Redis通道。
ChatApp/settings.py
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer"
}
}
第15步:现在,我们需要创建2个用户,为此我们将使用 “python manage.py createsuperuser“命令,在系统中创建一个超级用户。
第16步:我们设置了参数LOGIN_REDIRECT_URL = “chat-page”,这是我们登陆页面URL的名称。这意味着,只要用户登录,他就会被送到chatPage,成为一个经过验证的用户,他就有资格通过聊天。现在我们同样需要为网站设置LOGOUT_REDIRECT_URL。
ChatApp/settings.py
部署
现在,运行你的服务器并移动到网站上,启动两个不同的浏览器来登录其他两个用户。这是因为,如果你用第一个用户的凭证登录,登录的详细信息就会储存在cookie中,那么如果你在同一个浏览器中用第二个用户的详细信息登录,即使是用不同的标签,所以,你不能在同一个浏览器中与其他两个用户聊天,这就是为什么要使用两个不同的浏览器。