使用Django的实时聊天应用程序

使用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进一步工作。

使用Django的实时聊天应用程序

第5步:安装django-channels,以便与聊天应用程序一起工作。这将在你的环境中安装通道。

python -m pip install -U channels

使用Django的实时聊天应用程序

第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

使用Django的实时聊天应用程序

第8步:创建一个新的应用程序,将拥有所有的聊天功能。为了创建一个应用程序,在终端写一个命令。

python manage.py startapp chat

并在settings.py中把你的应用程序添加到已安装的应用程序中。

使用Django的实时聊天应用程序

第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}}没有打印出任何东西。

使用Django的实时聊天应用程序

  • 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“命令,在系统中创建一个超级用户。

使用Django的实时聊天应用程序

第16步:我们设置了参数LOGIN_REDIRECT_URL = “chat-page”,这是我们登陆页面URL的名称。这意味着,只要用户登录,他就会被送到chatPage,成为一个经过验证的用户,他就有资格通过聊天。现在我们同样需要为网站设置LOGOUT_REDIRECT_URL。

ChatApp/settings.py

使用Django的实时聊天应用程序

部署

现在,运行你的服务器并移动到网站上,启动两个不同的浏览器来登录其他两个用户。这是因为,如果你用第一个用户的凭证登录,登录的详细信息就会储存在cookie中,那么如果你在同一个浏览器中用第二个用户的详细信息登录,即使是用不同的标签,所以,你不能在同一个浏览器中与其他两个用户聊天,这就是为什么要使用两个不同的浏览器。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程