Встановлення
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
З моделлю.
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')