Как я пытался подружить Django и Websockets

от автора

Когда браузер клиенту нужно постоянное обновление данных с сервера, на ум сразу приходят сокеты. Но после множества просмотренных мной гайдов по данной теме, я не нашел ничего одновременно и актуального, и с нормальными объяснениями ну или хотя бы работающего. В итоге просидев пару-тройку часов у меня получилось собрать пазл из миллиона статей с Хабра и пары видеороликов от моих коллег из Индии.

Установка

В этой статье я не буду рассказывать как устанавливать Django, создавать модели и классы представлений. Предполагается, что у вас уже есть готовый проект и вы просто хотите впилить в него сокеты.

Python

pip install channels

pip install channels-redis

pip install daphne

Если при установке daphne пип выдает ошибку про C++, то скачиваем BuildTools и во время установки выбираем «Разработка классических приложений на C++». Ребутаем комп и снова запускаем pip install.

Шаг №1

В файл settings.py добавляем загруженные пакеты(Причем исходя из документации — daphne надо добавить обязательно в начало списка):

INSTALLED_APPS = [   'daphne',   'channels',   ... ]

В этом же файле изменяем:

WSGI_APPLICATION = 'yourapp.wsgi.application'

на:

ASGI_APPLICATION = 'yourapp.asgi.application'

Добавляем данные Channel layers в settings.py, конфигурация зависит от того, где у вас стоит Redis, подробнее тут. Для тестов я использовал такой сетап:

CHANNELS_LAYERS = {   'default': {     'BACKEND': 'channels.layers.InMemoryChannelLayer'   } } 

Шаг №2

Создаем файл consumers.py в директории нашего приложения. Если вы работали с Class Based Views в Django то Consumers вам покажется знакомым.

Создаем свой обработчик:

from channels.consumer import AsyncConsumer   YourConsumer(AsyncConsumer):      async def websocket_connect(self, event):         await self.send({"type": "websocket.accept"})      async def websocket_recieve(self, text_data):         await self.send({             "type": "websocket.send",             "text": "Hello from Django socket"         })      async def websocket_disconnect(self, event):         pass

Каждый метод класса YourConsumer отвечает за свой тип запросов через сокеты. Очень похоже на CBV

Шаг №3

Изменяем данные файла asgi.py в корневой директории проекта:

from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter from channels.routing import URLRouter  from django.core.asgi import get_asgi_application from django.urls import path   import os os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yourapp.settings')   from yourapp.consumers import YourConsumer   django_asgi_app = get_asgi_application()  application = ProtocolTypeRouter({     'http': django_asgi_app,     'websocket': AuthMiddlewareStack(         URLRouter([             path('ws', YourConsumer.as_asgi())         ])     ) })

В URLRouter мы прописываем путь, по которому будет доступен Consumer. Например в данном случае для 127.0.0.1 это — ws://127.0.0.1:8000/ws. Это очень похоже на концепцию urls.py, кстати при желании можно вынести все пути в отдельный файл routers.py и импортировать их потом в корневую директорию.

Тестируем

Запускаем локалку и отправляем запросы к сокету. Например через Js:

const socket = new WebSocket('ws://127.0.0.1:8000/ws');  socket.onopen = function(e) {   socket.send(JSON.stringify({     message: 'Hello from Js client'   })); };  socket.onmessage = function(event) {   try {     console.log(event);   } catch (e) {     console.log('Error:', e.message);   } };
Запускаем запрос и видим ответ который мы прописывали в consumers.py
Запускаем запрос и видим ответ который мы прописывали в consumers.py

Ссылки

Документация

Гитхаб

Мой тг


ссылка на оригинал статьи https://habr.com/ru/post/700100/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *