DevGang
Авторизоваться

WebSocket в Django

Что такое WebSocket?

Ну, это протокол компьютерной связи, который используется для двунаправленной связи.

Он может использоваться сервером для отправки данных клиенту.

Короче говоря, они используются для связи в реальном времени.

Пример: чат-приложения, синхронизация данных в реальном времени в играх, SSE и т.д.

Хотя WebSocket кажется крутым, он может быть слишком перегружен для простых приложений, таких как приложение погоды, которое опрашивает данные через несколько интервалов.

Как вы можете реализовать их в Django?

Что ж, это легко, мы можем использовать пакет python, называемый channels.

Они созданы теми же разработчиками, что и Django.

Просто откройте терминал и введите следующее, чтобы установить channels

python -m pip install -U channels["daphne"]

Он установит каналы и специальный веб-сервер ASGI под названием daphne, который используется для обработки протокола ws://.

Теперь в этом посте мы будем делать индикатор присутствия пользователя в сети. Это приложение сообщит клиенту количество пользователей, подключенных к серверу.

Также мы не будем использовать channel_layer для трансляции сообщения, так как это слишком усложнит ситуацию.

Создайте приложение django

django-admin startproject django_websocket

Перейдите в папку django_websocket и введите эту команду, чтобы создать новое приложение django.

cd django_websocket
python manage.py startapp presence

В settings.py добавьте сервер daphne и наше приложение presence.

Добавьте daphne сверху.
# django_websocket/settings.py

INSTALLED_APPS = [
    'daphne' # ⬅ ASGI Webserver
    'presence', # ⬅ Our Custom App
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Добавьте следующие строки в settings.py, чтобы позволить django также обрабатывать ASGI, а также обрабатывать перенаправление входа и выхода.

# django_websocket/settings.py
# Daphne Conf
ASGI_APPLICATION = "django_websocket.asgi.application"

LOGIN_REDIRECT_URL = "index"
LOGOUT_REDIRECT_URL = "index"

Теперь добавим код в asgi.py и заменим его следующим:

import os

from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter # <- Add this
from channels.auth import AuthMiddlewareStack # <- Add this
from presence.consumers import PresenceConsumer # <- Add this

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_websockets.settings')

application = ProtocolTypeRouter(
    {
        "http": get_asgi_application(),
        "websocket": AuthMiddlewareStack(
            PresenceConsumer.as_asgi()
        ),
    }
)

Давайте разберем это:

  • С 1 по 6 строчку импортируем все необходимые модули
  1. os: Для установки некоторых необходимых переменных окружения
  2. get_asgi_application: Просто обрабатывает http-сторону нашего приложения.
  3. ProtocolTypeRouter: Позвольте нам обрабатывать различные типы интерфейсов протоколов, например, для http мы просто будем делать вещи http, но для соединений протокола WebSocket мы позволим нашему приложению обрабатывать часть веб-сокетов.
  4. AuthMiddlewareStack: Поддерживает стандартную аутентификацию Django, при которой данные пользователя хранятся в сеансе. Он разрешает доступ только для чтения к пользовательскому объекту в scope. Короче говоря, это позволяет нам получить текущий request.user.
  5. PresenceConsumer: Это наш заказной потребитель ASGI, который будет обрабатывать веб-сокеты. Мы будем делать это дальше
  6. В строке 8 мы добавляем файл, который добавляет проект settings.py в окружение с именем DJANGO_SETTINGS_MODULE. Нам не о чем беспокоиться.
  7. В строке с 10 по 17 мы объявляем приложение. Это то же самое "django_websocket.asgi.application.
  • Приложение эквивалентно ProtocolTypeRouter, который принимает словарь протоколов, таких как http и websocket.
  • WebSocket использует функцию, называемую AuthMiddlewareStack, которая использует наших потребителей

Теперь мы переходим к интересной части, которая делает самих потребителей WebSocket.

Во-первых, создайте имя файла Consumers.py в папке presence и напишите в нем этот код.

import json

from channels.generic.websocket import WebsocketConsumer


class PresenceConsumer(WebsocketConsumer):

    connections = []

    def connect(self):
        self.accept()
        self.user = self.scope["user"]
        self.connections.append(self)
        self.update_indicator(msg="Connected")

    def disconnect(self, code):
        self.update_indicator(msg="Disconnected")
        self.connections.remove(self)
        return super().disconnect(code)

    def update_indicator(self, msg):
        for connection in self.connections:
            connection.send(
                text_data=json.dumps(
                    {
                        "msg": f"{self.user} {msg}",
                        "online": f"{len(self.connections)}",
                        "users": [f"{user.scope['user']}" for user in self.connections],                        
                    }
                )
            )

Этот код может показаться пугающим, но на самом деле он довольно прост, если вы понимаете основную концепцию потребителей.

Давайте сломаем это.

  • Со строк 1 по 3 мы импортируем некоторые важные модули, такие как json и WebsocketConsumer.
  • В строке 6 мы создаем класс с именем PresenceConsumer, который является расширением класса WebsocketConsumer.

WebsocketConsumer дает нам некоторые методы для WebSocket, такие как

  1. метод connect вызывается, когда клиент подключается к WebSocket. Если мы переопределяем этот метод, нам нужно добавить self.accept().
  2. Метод disconnect вызывается, когда клиент отключается от WebSocket.
  3. Метод receive вызывается, когда клиент отправляет запрос в WebSocket.
  • Метод с именем update_indicator — это пользовательский метод, который мы создали.
  • connections — это пустой список, в котором будут храниться подключенные пользователи.

В методе connect мы переопределяем методы, поэтому, когда новый пользователь подключается к WebSocket, мы получаем пользователя из сеанса с помощью этого кода self.user = self.scope["user"], так как мы обернули наш PreescenceConsumer в AuthMiddlewareStack, мы можем получить текущего пользователя из сеанса и сохранить его в файле self.user. После того, как мы получили пользователя, мы собираемся добавить текущее пользовательское соединение к connection, а затем вызвать self.update_indicator и передать msg как connected.

Теперь, если мы посмотрим на update_indicator, мы увидим, что мы перебираем список connection и отправляем всем клиентам данные json.

Пример:

Если пользователь с именем Sid подключается к WebSocket с 3 пользователями, подключенными, все клиенты, подключенные к WebSocket, включая Sid, получат следующий json

{
    "msg": "Sid Connected",
    "online": 4,
    "users" [
                "user1",
                "user2",
                "user3",
                "Sid"
            ]
}

Теперь мы обработаем некоторые из наших представлений для отображения индексной страницы и страницы входа. Просто добавьте эти 4 строки кода, и все готово.

from django.shortcuts import render

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

Да, это все.

Теперь мы займемся маршрутизацией WebSocket.

Это похоже на URL-адрес, отображающий ваши представления в типичном приложении django.

Теперь в основном в очень больших приложениях мы должны просто разделить маршрутизацию представлений и маршрутизацию ws, но здесь, чтобы сэкономить время, мы просто напишем это в urls.py

Сначала создайте файл urls.py в приложении presence и добавьте следующий код.

from django.urls import path

from . import consumers
from . import views
from django.contrib.auth.views import LoginView, LogoutView


urlpatterns = [
    path('', views.index, name="index"),
    path('login/', LoginView.as_view(template_name='login.html'), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
    path("ws/presence", consumers.PresenceConsumer.as_asgi()),
]

Здесь мы создаем сопоставление URL-адресов для наших представлений и WebSocket.

Как видите, мы также используем представления аутентификации django, чтобы получить функции входа и выхода в нашем приложении.

Чтобы добавить WS Consumer, нам просто нужно отобразить его в urlpatterns, как и любые другие представления на основе классов, только вместо .as_view() здесь мы пишем .as_asgi().

Это была backend часть, теперь нам нужно настроить нашу frontend часть.

Теперь давайте перейдем к frontend части

  • Создайте каталог template в приложении presence и создайте два файла с именами index.html и login.html.

В index.html напишите следующий код.

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>Online Presence Indicator</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
</head>

<body>
  <div class="container">
    <section class="section">
      <div class="columns">
        <div class="column">

          {% if user.is_authenticated %}
          Hello <strong>{{ user }}</strong>
          <br>
          <a href="{% url 'logout' %}">Logout</a>
          {% else %}
          <a href="{% url 'login' %}">Login</a>
          {% endif %}
          <h1 class="title">Online Presence Indicator</h1>
          <div id="presence"><span class="tag is-success" id="pre_cnt">0</span> users online</div>
          <ul id="messages"></ul>
        </div>
        <div class="column">
          <div class="box">
            <h1 class="title">Online Users</h1>
            <div id="online-users"></div>
          </div>
        </div>
      </div>
    </section>
  </div>

  <script>
    const ws = new WebSocket('ws://localhost:8000/ws/presence/');
    const presenceEl = document.getElementById('pre_cnt');
    const messagesEl = document.getElementById('messages');
    const onlineUsers = document.querySelector("#online-users");

    ws.onmessage = (event) => {
      onlineUsers.innerHTML = "";
      let data = JSON.parse(event.data)
      presenceEl.innerHTML = data.online;
      const li1 = document.createElement('li');
      li1.innerHTML = data.msg;
      messagesEl.appendChild(li1);
      data.users.forEach(user => {
        const li2 = document.createElement("li");
        li2.classList.add("on-us")
        li2.innerHTML = user;
        onlineUsers.appendChild(li2);
      });

    };
  </script>
</body>

</html>

Здесь мы используем Bulma для стилизации нашей страницы, вы можете использовать свою собственную таблицу стилей, если хотите.

Главное, что мы должны здесь посмотреть, это javascript.

  • Во-первых, мы создаем объект WebSocket и передаем URL-адрес WebSocket.
  • Далее мы сохраняем элементы html с помощью getElementById.
  • Затем мы обработаем событие WebSocket.onmessage для сообщений в реальном времени с сервера. Event содержит атрибут data, к которому мы будем использовать JSON.parse для преобразования приходящего JSON в объект.
  • Когда WebSocket получит сообщение от службы, мы покажем, что пользователь подключен в div #messages и добавим пользователя в div #online-users.

Теперь давайте просто быстро создадим нашу страницу входа.

Откройте login.html и напишите этот html.

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>Online Presence Indicator</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
</head>

<body>
  <div class="container">
    <section class="section">
        <h1 class="title">Login</h1>
      <form action="." method="post">
        {{form.as_p}}
        {% csrf_token %}
        <br>
        <button class="button">Login</button>
      </form>
    </section>
  </div>
</body>

</html>

Вот и все, теперь наше приложение готово.

Давайте теперь проверим это, запустив сервер

python manage.py runserver

Вы должны увидеть следующий поток.

Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
May 23, 2023 - 22:54:21
Django version 4.2.1, using settings 'myproject.settings'
Starting ASGI/Daphne version 4.0.0 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Мы видим, что Django использует сервер ASGI/Daphne для запуска этого веб-сайта.

Теперь откройте http://localhost:8000, и вы должны увидеть следующую страницу.

Индексная страница с неавторизованным пользователем
Индексная страница с неавторизованным пользователем

Вы увидите, что в окне сообщения будет написано AnonymousUser Connected.

Давайте создадим учетную запись и протестируем ее.

python manage.py createsuperuser

Теперь, когда мы создали суперпользователя, давайте войдем под этой учетной записью и посмотрим.

Страница авторизации
Страница авторизации
Индексная страница с авторизованным пользователем
Индексная страница с авторизованным пользователем

Теперь откройте частную страницу и откройте обе страницы в режиме разделенного просмотра.

Демонстрационное видео

Вот ссылка GitHub для исходного кода этого приложения: https://github.com/foxy4096/django_websocket_demo

Так что за дело, ребята.

Источник:

#Python #Django
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу