Как настроить работу с вебсокетами в проекте, разделенном на backend (DRF) и frontend (React)? В полном Django проекте я использовал основанный на Django Channels подход, но сейчас нужно интегрировать компоненты на React и Django REST framework. Как правильно организовать взаимодействие через вебсокеты в DRF? Можно ли применить подход с Channels или следует использовать другие методы с учетом событийного подхода, предпочтительного для React?
Для настройки использования веб-сокетов в проекте с Django REST framework в качестве backend и React в качестве frontend, можно использовать библиотеку Django Channels для обработки веб-сокетов совместно с DRF. Вот общий план действий для настройки веб-сокетов в таком проекте: 1. Установите библиотеку Django Channels: ``` pip install channels ``` 2. Создайте файл routing для веб-сокетов в вашем Django проекте: ```python # myapp/routing.py from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter from django.urls import path from myapp import consumers application = ProtocolTypeRouter({ 'websocket': AuthMiddlewareStack( URLRouter([ path('ws/chat/', consumers.ChatConsumer), ]), ), }) ``` 3. Создайте consumer для обработки веб-сокетов: ```python # myapp/consumers.py 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=None, bytes_data=None): # Обработка сообщения от frontend ``` 4. Используйте ваш consumer для взаимодействия с веб-сокетами в вашем Django проекте, например, через сигналы или другие методы. 5. На стороне frontend с React создайте соединение с веб-сокетом, например, используя WebSocket API: ```javascript const socket = new WebSocket('ws://localhost:8000/ws/chat/'); ``` 6. Организуйте отправку и получение сообщений через веб-сокеты на frontend. Это общий план действий, который позволит вам настроить работу с веб-сокетами в проекте с DRF и React. Помните, что вам также потребуется настроить CORS и обработку разрешений для веб-сокетов на стороне Django.
DRF это REST API, т.е. там архитектура построена на HTTP запросах, а в вебсокет их нет, получает что DRF тут боком и для вебсокетов он вообще не нужен. Правильно - вынести бизнес логику из DRF в отдельный слой, а в вебсокетах дергать только эту логику и ничего не знать про DRF
UPD: Так же такой вариант, если REST API уже реализован, то написать для вебсокетов отдельный прокси, лучше не на джанго, а на starlette (fastapp скорее тут будут излишен), aiohttp или другом async фреймворки, который умеет в вебсокет. Так вот, этот прокси и будет уже дергать эндпоинты твой DRF, реальными http запросам
Я в проде запускал отдельный экземпляр приложения для web socket. Channels отлично справляется со своей задачей. Документация отлично описана, да и много гайдов есть.
Как пример:
asgi.py
import os from django.core.asgi import get_asgi_application from channels.routing import ProtocolTypeRouter, URLRouter from channels.security.websocket import AllowedHostsOriginValidator os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') get_asgi_app = get_asgi_application() from notification import routing application = ProtocolTypeRouter({ "http": get_asgi_app, "websocket": AllowedHostsOriginValidator( URLRouter( routing.websocket_urlpatterns ) ), })
routing.py
from django.urls import path from . import consumers websocket_urlpatterns = [ path(r'wss/notification/<str:room_name>/', consumers.NotificationConsumer.as_asgi()), ]
consumers.py
import json from channels.generic.websocket import AsyncWebsocketConsumer from service.auth.auth import get_auth class NotificationConsumer(AsyncWebsocketConsumer): user = None async def connect(self): user = await get_auth(self.scope.get('query_string')) self.room_name = self.scope['url_route']['kwargs']['room_name'] max_connections_allowed = 1 user_connections_key = f'user_connections_{user.id}' current_connections = self.scope.get(user_connections_key, 0) if current_connections >= max_connections_allowed: await self.close() else: self.scope[user_connections_key] = current_connections + 1 await self.channel_layer.group_add( self.room_name, self.channel_name ) if user.is_access_allowed(): self.user = user await self.accept() async def disconnect(self, close_code): pass async def receive(self, text_data): await self.send_notification({'message': text_data}) async def send_notification(self, message): message = message["message"] await self.send(text_data=json.dumps({"message": message})) def send_notification(username, message, type_notification, created_at, id_notification, options=None): channel_layer = get_channel_layer() async_to_sync(channel_layer.group_send)( f'notification-{username}', {'type': 'send.notification', 'message': {'message': message, 'type_notification': type_notification, 'id': id_notification, 'created_at': created_at, 'notification_options': options}})
на фронте можно использовать нативные методы (образец из vue)
const socket = ref(null) socket.value = new WebSocket( `ws://127.0.0.1:8000/wss/notification/notification-${user.username}/}` ) socket.value.onopen = () => { console.log("otkrit") } socket.value.onclose = () => { console.log("zakrit") } socket.value.onerror = () => { console.log("error") } socket.value.onmessage = (message) => { const data = JSON.parse(message.data) // тут делаем что-то с данными })