Django channels.
Basics of Python and Django. -> Настраниваем django channels для работы по веб-сокетам.
Настраниваем django channels для работы по веб-сокетам.
Установка.
mkdir app
cd app
python3 -m venv venv
. ./venv/bin/activate
Создаем requirements.txt
Django==3.0.7
channels
redis==3.2.0
channels_redis==2.4.2
Устанавливаем зависимости.
pip install -r requirements.txt
Создадим новый проект
django-admin startproject server
Создадим новое приложение connection.
cd server
./manage.py startapp connection
C моделью.
from django.db import models
class SocketConnection(models.Model):
sid = models.CharField(max_length=250, db_index=True, null=True, unique=True)
@staticmethod
def create_if_not_exist(data):
try:
SocketConnection.objects.get(sid=data['sid'])
except:
SocketConnection.objects.create( \
sid = data['sid'],
)
Еще мы создали статический метод для создания записи.
Админка.
from django.contrib import admin
from connection.models import SocketConnection
@admin.register(SocketConnection)
class SocketConnectionAdmin(admin.ModelAdmin):
list_display = [
'sid'
]
Для очистки таблицы при каждом запуске сервера добавим хук ready в connection/apps.py
from django.apps import AppConfig
class ConnectionConfig(AppConfig):
name = 'connection'
def ready(self):
from connection.models import SocketConnection
SocketConnection.objects.all().delete()
И пропишем в connection/init.py
default_app_config = 'connection.apps.ConnectionConfig'
Далее подключаем все в settings.
INSTALLED_APPS = [
...
'channels',
'connection'
]
Создадим файл server/server/channels_app.py под приложение channels.
При этом наш проект называется server.
from channels.routing import ProtocolTypeRouter, URLRouter
from card.card_consumer import CardConsumer
from django.urls import re_path
from channels.auth import AuthMiddlewareStack
websocket_urlpatterns = [
re_path(r'card/$', CardConsumer),
]
application = ProtocolTypeRouter({
'websocket': AuthMiddlewareStack(
URLRouter(
websocket_urlpatterns
)
),
})
Создадим новое приложение card и включим его в settings.py
./manage.py startapp card
Далее в нем создадим класс консьюмера server/card/card_consumer.py, обслуживающего сокет соединения.
from channels.generic.websocket import WebsocketConsumer
import json
from asgiref.sync import async_to_sync
from connection.models import SocketConnection
class CardConsumer(WebsocketConsumer):
sid = None;
def connect(self):
print('Connnect!!! %s' % self.channel_name)
self.accept()
self.sid = self.channel_name
SocketConnection.create_if_not_exist({'sid': self.channel_name})
def disconnect(self, close_code):
print('DISCONNECT!!!')
try:
SocketConnection.objects.get(sid = self.sid).delete()
except:
pass
def receive(self, text_data):
message = json.loads(text_data)
print(message)
def test_hendler(self,event):
message = { \
'type': 'test_hendler', \
'message': 'test' \
}
self.send(text_data=json.dumps(message))
test_hendler - функция, которая посылает сообщение в сокет когда мы посылаем его из любой части джанго приложения методом:
async_to_sync(channel_layer.send)('socketId', {'type': 'test_hendler'})
Включим приложение в настройки.
ASGI_APPLICATION = "server.channels_app.application"
Добавим слой транспорта для channels.
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("127.0.0.1", 6379)],
},
},
}
При этом должен быть установлен REDIS сервер.
При запуске должны увидеть такую картину.
Клиентская часть.
Определим папку для шаблонов в настройках.
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [ os.path.join(BASE_DIR, 'templates')],
...
Создадим шаблон server/templates/index.html.
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
</head>
<body>
<h1>Test page</h1>
<input type="text" id="text" />
<button id="sendButton">Send message by JS</button>nt
<a href="/send" target="_blank">Send message from server</a>
<div id="messages"></div>
<script>
var timer = null;
connect = () => {
clearInterval(timer);
webSocket = new WebSocket('ws://localhost:7777/card/');
webSocket.onerror = (evt) => {
timer = setTimeout(connect,1000);
}
webSocket.onmessage = (event) => {
var message = JSON.parse(event.data)
$('#messages').append(message.message);
}
webSocket.onclose = (event) => {
console.log('Close connection');
timer = setTimeout(connect,1000);
};
webSocket.onopen = (event) => {
console.log('Connection established');
};
}
$('#sendButton').on('click', () => {
var message = $('#text').val();
var data = {
'type': 'test_hendler',
'data': message
}
webSocket.send(JSON.stringify(data));
})
connect();
</script>
</body>
</html>
Сделаем вьюху server/card/views.py
from django.shortcuts import render
def index(request):
return render(request,'index.html')
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from connection.models import SocketConnection
def send(request):
channel_layer = get_channel_layer()
for c in SocketConnection.objects.all():
print('Sending to %s' % c.sid)
async_to_sync(channel_layer.send)(c.sid, {'type': 'test_hendler'})
return render(request,'send.html')