Как настроить работу с вебсокетами в проекте, разделенном на 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 <br/> <br/> UPD: Так же такой вариант, если REST API уже реализован, то написать для вебсокетов отдельный прокси, лучше не на джанго, а на starlette (fastapp скорее тут будут излишен), aiohttp или другом async фреймворки, который умеет в вебсокет. Так вот, этот прокси и будет уже дергать эндпоинты твой DRF, реальными http запросам
Я в проде запускал отдельный экземпляр приложения для web socket. Channels отлично справляется со своей задачей. Документация отлично описана, да и много гайдов есть. <br/> Как пример: <br/> asgi.py <br/> <pre><code class="python">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
)
),
})</code></pre> <br/> <br/> routing.py <br/> <pre><code class="python">from django.urls import path
from . import consumers
websocket_urlpatterns = [
path(r'wss/notification/<str:room_name>/', consumers.NotificationConsumer.as_asgi()),
]</code></pre> <br/> <br/> consumers.py <br/> <pre><code class="python">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}})</code></pre> <br/> <br/> на фронте можно использовать нативные методы (образец из vue) <br/> <pre><code class="javascript">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)
// тут делаем что-то с данными
})</code></pre>