The use of django channels (websocket) to realize real-time chat between web pages

Table of contents

1. Create project mysite and apply chat

1. Create project mysite

2. Create an application chat and add the application to INSTALLED_APPS of settings.py

3. Add template files

4. Add views and routes

5. Configure the root route to specify the route of the chat application

6. Integrated channels

Second, implement the chat server

1. Create a new file chat/templates/chat/room.html and add the following content

2. Create view room and configure routing

3. Configure consumer consumers.py

4. Configure routings.py

5. Configure asgi.py again

6. Enable channel layer CHANNEL_LAYERS

7. Configure consumers.py again

3. Improvement: rewrite the consumer as asynchronous


Version

python==3.7

django==3.2.18

channels==3.0.3
channels-redis==4.0.0

1. Create project mysite and apply chat

1. Create project mysite

Use pycharm to create project mysite

Create the project mysite with the command

django-admin startproject mysite

2. Create an application chat and add the application to INSTALLED_APPS of settings.py

 python manage.py startup app chat

# settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'chat',
]

 

 The directory structure of the project after completion:

3. Add template files

Create a new templates folder under the chat application, and right-click to select Mark Direcory as >> Template Folder, the templates folder will turn purple, and create an index.html file

 index.html add the following content

<!-- chat/templates/chat/index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Chat Rooms</title>
</head>
<body>
    What chat room would you like to enter?<br>
    <input id="room-name-input" type="text" size="100"><br>
    <input id="room-name-submit" type="button" value="Enter">

    <script>
        document.querySelector('#room-name-input').focus();
        document.querySelector('#room-name-input').onkeyup = function(e) {
            if (e.keyCode === 13) {  // enter, return
                document.querySelector('#room-name-submit').click();
            }
        };

        document.querySelector('#room-name-submit').onclick = function(e) {
            var roomName = document.querySelector('#room-name-input').value;
            window.location.pathname = '/chat/' + roomName + '/';
        };
    </script>
</body>
</html>

4. Add views and routes

# chat/views.py
from django.shortcuts import render


def index(request):
    return render(request, "index.html")
# chat/urls.py
from django.urls import path

from . import views


urlpatterns = [
    path("", views.index, name="index"),
]

5. Configure the root route to specify the route of the chat application

# mysite/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("chat/", include("chat.urls")),
    path("admin/", admin.site.urls),
]

Start the django project at this time, and the browser visits http://127.0.0.1:8000/chat/

 

 Here, after entering the string in the input box and clicking Enter, a 404 error will be reported, so let's continue to configure

6. Integrated channels

a. Install channels

pip3 install -i https://pypi.douban.com/simple channels==3.0.3

 Specify to install channels==3.0.3, do not install the latest 4.0 version, otherwise websocket will report a 404 error when connecting

b. Adjust the code in mysite/asgi.py

import os

from channels.routing import ProtocolTypeRouter
from django.core.asgi import get_asgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

application = ProtocolTypeRouter(
    {
        "http": get_asgi_application(),
    }
)

c. Add channels application and configure ASGI_APPLICATION in settings.py

# mysite/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'chat',
    'channels'
]

ASGI_APPLICATION = "mysite.asgi.application"

Second, implement the chat server

1. Create a new filechat/templates/chat/room.html,并添加以下内容

<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Chat Room</title>
</head>
<body>
    <textarea id="chat-log" cols="100" rows="20"></textarea><br>
    <input id="chat-message-input" type="text" size="100"><br>
    <input id="chat-message-submit" type="button" value="Send">
    {
   
   { room_name|json_script:"room-name" }}
    <script>
        const roomName = JSON.parse(document.getElementById('room-name').textContent);

        const chatSocket = new WebSocket(
            'ws://'
            + window.location.host
            + '/ws/chat/'
            + roomName
            + '/'
        );

        chatSocket.onmessage = function(e) {
            const data = JSON.parse(e.data);
            document.querySelector('#chat-log').value += (data.message + '\n');
        };

        chatSocket.onclose = function(e) {
            console.error('Chat socket closed unexpectedly');
        };

        document.querySelector('#chat-message-input').focus();
        document.querySelector('#chat-message-input').onkeyup = function(e) {
            if (e.keyCode === 13) {  // enter, return
                document.querySelector('#chat-message-submit').click();
            }
        };

        document.querySelector('#chat-message-submit').onclick = function(e) {
            const messageInputDom = document.querySelector('#chat-message-input');
            const message = messageInputDom.value;
            chatSocket.send(JSON.stringify({
                'message': message
            }));
            messageInputDom.value = '';
        };
    </script>
</body>
</html>

2. Create view room and configure routing

# chat/views.py
from django.shortcuts import render


def index(request):
    return render(request, "chat/index.html")


# 新添加
def room(request, room_name):
    return render(request, "room.html", {"room_name": room_name})
# chat/urls.py
from django.urls import path

from . import views


urlpatterns = [
    path("", views.index, name="index"),
    path("<str:room_name>/", views.room, name="room"),  # 新添加
]

At this time, start the django project, open the console in the browser and enter the address http://127.0.0.1:8000/chat/

Type in myroom and Enter

 Enter a message and click Send and nothing happens, so go ahead and configure consumers

3. Configure consumer consumers.py

Add a new one under the chat applicationconsumers.py文件,并添加以下内容

# chat/consumers.py
import json

from channels.generic.websocket import WebsocketConsumer


class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.accept()

    def disconnect(self, close_code):
        pass

    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json["message"]

        self.send(text_data=json.dumps({"message": message}))

Directory Structure

 

4. Configure routings.py

Create a new routing.py file under the chat application and add the following content

# chat/routing.py
from django.urls import re_path

from . import consumers

websocket_urlpatterns = [
    re_path(r"ws/chat/(?P<room_name>\w+)/$", consumers.ChatConsumer.as_asgi()),
]

5. Configure asgi.py again

# mysite/asgi.py
import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application

from chat.routing import websocket_urlpatterns

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
# Initialize Django ASGI application early to ensure the AppRegistry
# is populated before importing code that may import ORM models.
django_asgi_app = get_asgi_application()

import chat.routing

application = ProtocolTypeRouter(
    {
        "http": django_asgi_app,
        "websocket": AllowedHostsOriginValidator(
            AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
        ),
    }
)

Run the migration command

python manage.py migrate

 At this time, start the django project, open the console in the browser to access http://127.0.0.1:8000/, enter myroom and enter, and no longer report the error of ws connection 500

 At this time, enter a message and click Send, the message will appear in the chat box, but when you open a new tab and enter the same URL (http://127.0.0.1:8000/chat/myroom/), the message will not appear in the In the URL message chat box of the new tab page

 To be able to receive messages sent by another tab page, you need to continue to configure the startup channel layer CHANNEL_LAYERS

 

6. Enable channel layer CHANNEL_LAYERS

  • Install channels-redis first

pip3 install -i https://pypi.douban.com/simple channels-redis==4.0.0

  • Add CHANNEL_LAYERS configuration in settings.py (please confirm that redis has been installed before this)
# settings.py
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],
        },
    },
}
  • Make sure the channel layer can communicate with Redis. Open the Django shell and run the following command:  
$ python3 manage.py shell
>>> import channels.layers
>>> channel_layer = channels.layers.get_channel_layer()
>>> from asgiref.sync import async_to_sync
>>> async_to_sync(channel_layer.send)('test_channel', {'type': 'hello'})
>>> async_to_sync(channel_layer.receive)('test_channel')
{'type': 'hello'}

 

7. Configure consumers.py again

Replace the code in chat/consumers.py with the following code

# chat/consumers.py
import json

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer


class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
        self.room_group_name = f"chat_{self.room_name}"

        # Join room group
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name, self.channel_name
        )

        self.accept()

    def disconnect(self, close_code):
        # Leave room group
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name, self.channel_name
        )

    # Receive message from WebSocket
    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json["message"]

        # Send message to room group
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name, {"type": "chat.message", "message": message}
        )

    # Receive message from room group
    def chat_message(self, event):
        message = event["message"]

        # Send message to WebSocket
        self.send(text_data=json.dumps({"message": message}))

At this time, start the django project, open two tab pages in the browser, and enter http://127.0.0.1:8000/chat/myroom/ , send the message again, and the other tab page will receive it~

 So far, a basic full-featured chat server is complete

3. Improvement: rewrite the consumer as asynchronous

The code in consumers.py we wrote before is all synchronous. Synchronous uses are convenient because they can call regular synchronous I/O functions such as those that access Django models without writing special code. However, asynchronous consumers can provide a higher level of performance, because they do not need to create additional threads when processing requests.

So we rewrite the code in chat/consumers.py again, replacing the synchronous code written before with the following code

# chat/consumers.py
import json

from channels.generic.websocket import AsyncWebsocketConsumer


class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
        self.room_group_name = f"chat_{self.room_name}"

        # Join room group
        await self.channel_layer.group_add(self.room_group_name, self.channel_name)

        await self.accept()

    async def disconnect(self, close_code):
        # Leave room group
        await self.channel_layer.group_discard(self.room_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 room group
        await self.channel_layer.group_send(
            self.room_group_name, {"type": "chat.message", "message": message}
        )

    # Receive message from room group
    async def chat_message(self, event):
        message = event["message"]

        # Send message to WebSocket
        await self.send(text_data=json.dumps({"message": message}))

This new code for ChatConsumer is very similar to the original code, with the following differences:

  • ChatConsumerNow inherits from AsyncWebsocketConsumer, instead of WebsocketConsumer.
  • All methods are async defand not only def.
  • awaitUsed to call asynchronous functions that perform I/O.
  • async_to_sync is no longer required when calling methods at the channel layer.

At this time, start the django project again, open two tabs in the browser, and enter http://127.0.0.1:8000/chat/myroom/ , and send the message again. At this time, the chat server is completely asynchronous

Reference:  Tutorial — Channels 4.0.0 documentation

Guess you like

Origin blog.csdn.net/qq_37140721/article/details/130219933
Recommended