Когда браузер клиенту нужно постоянное обновление данных с сервера, на ум сразу приходят сокеты. Но после множества просмотренных мной гайдов по данной теме, я не нашел ничего одновременно и актуального, и с нормальными объяснениями ну или хотя бы работающего. В итоге просидев пару-тройку часов у меня получилось собрать пазл из миллиона статей с Хабра и пары видеороликов от моих коллег из Индии.
Установка
В этой статье я не буду рассказывать как устанавливать 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
class YourConsumer(AsyncConsumer):
async def websocket_connect(self, event):
await self.send({"type": "websocket.accept"})
async def websocket_receive(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', 'yourproject.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);
}
};
