Real-Time Communication with Sockets
In today’s discussion, we delve into the realm of real-time communication between the back end and front end. We understand that a continuous data flow fosters a better user experience. However, without delving superficially into less sophisticated techniques such as API polling, we will focus directly on the power of socket-based communication. Python and Django natively support this approach, which involves a bidirectional protocol enabling a constant data stream between the sender and recipient.
Stay tuned to explore the fascinating world of Django sockets and how they revolutionize real-time communication.
First of all let’s configure our environment.
Setting Up Your Django Project Directory
To get started, let’s create a directory for our project, which we’ll name ‘sockets.’ Next, we’ll update our package manager ‘pip’ and install the Django framework.
Follow these steps to set up your project directory:
- Create a project directory named ‘sockets’:
<span>mkdir </span>sockets<span>cd </span>sockets<span>mkdir </span>sockets <span>cd </span>socketsmkdir sockets cd sockets
Enter fullscreen mode Exit fullscreen mode
- Update your package manager ‘pip’:
python3.10 -m pip install --upgrade pippython3.10 -m pip install --upgrade pippython3.10 -m pip install --upgrade pip
Enter fullscreen mode Exit fullscreen mode
- Install the Django framework and Daphne:
pip install django, daphne, channelspip install django, daphne, channelspip install django, daphne, channels
Enter fullscreen mode Exit fullscreen mode
Hint: Daphne is an ASGI (ASGI (Asynchronous Server Gateway Interface) capable of managing WebSockets type connections and asynchronous communications
- Create and activate your virtual environment.
python3.10 -m venv vsocketsource vsocket/bin/activatepython3.10 -m venv vsocket source vsocket/bin/activatepython3.10 -m venv vsocket source vsocket/bin/activate
Enter fullscreen mode Exit fullscreen mode
When you run these commands you will see something similar to this.
- Create your django project and your django app with these commands:
django-admin startproject yourprojectnamedjango-admin startapp yourappnamedjango-admin startproject yourprojectname django-admin startapp yourappnamedjango-admin startproject yourprojectname django-admin startapp yourappname
Enter fullscreen mode Exit fullscreen mode
- Register your application and daphne in the settings.py file in the ‘INSTALLED_APPS’ array. It is extremely important that ‘daphne’ is in position 0 of the array.
# Application definitionINSTALLED_APPS = ['daphne','django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','yourappname']# Application definition INSTALLED_APPS = [ 'daphne', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'yourappname' ]# Application definition INSTALLED_APPS = [ 'daphne', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'yourappname' ]
Enter fullscreen mode Exit fullscreen mode
- Direct the entry point for asynchronous communications to the correct file.
# Still in settings file.WSGI_APPLICATION = 'yourprojectname.wsgi.application'ASGI_APPLICATION = 'yourprojectname.asgi.application'# Still in settings file. WSGI_APPLICATION = 'yourprojectname.wsgi.application' ASGI_APPLICATION = 'yourprojectname.asgi.application'# Still in settings file. WSGI_APPLICATION = 'yourprojectname.wsgi.application' ASGI_APPLICATION = 'yourprojectname.asgi.application'
Enter fullscreen mode Exit fullscreen mode
We will configure our ASGI server later.
*Now let’s configure our consumers, routing and models files.
- Firstly, let’s just create a model to serve data for our socket.
from django.db import modelsclass Random(models.Model):id = models.BigAutoField(primary_key=True)text = models.CharFiel(max_lenght=255, blank=True,null=True)class Queue(models.Model):id = models.BigAutoField(primary_key=True)status = models.IntegerField()from django.db import models class Random(models.Model): id = models.BigAutoField(primary_key=True) text = models.CharFiel(max_lenght=255, blank=True,null=True) class Queue(models.Model): id = models.BigAutoField(primary_key=True) status = models.IntegerField()from django.db import models class Random(models.Model): id = models.BigAutoField(primary_key=True) text = models.CharFiel(max_lenght=255, blank=True,null=True) class Queue(models.Model): id = models.BigAutoField(primary_key=True) status = models.IntegerField()
Enter fullscreen mode Exit fullscreen mode
Let’s apply this record to our database with the following commands.
python3 manage.py makemigrationspython3 manage.py migratepython3 manage.py makemigrations python3 manage.py migratepython3 manage.py makemigrations python3 manage.py migrate
Enter fullscreen mode Exit fullscreen mode
Hint: WebSocket Consumers in Django.
WebSocket consumers in Django are classes that handle server-side logic for real-time communications, such as WebSocket connections. They are responsible for handling the interactive and real-time features of Django web applications. Consumers respond to WebSocket events such as opening, closing, and message exchange, and are defined based on routes that map WebSocket URLs to specific consumers. They work with asynchronous code to efficiently manage multiple simultaneous connections.
- To create our consumer, we need create a file called ‘consumers.py
First we will create a Consumer class that will inherit the WebsocketConsumer class to handle WebSocket communications.
Next we will implement the connect method to provide the means of connection between the client and the server. After the connection is made, the client is accepted with the accept method, which allows the client to send and receive data.
Then we create a repeating block that will constantly update the number of records in the Random table in our database.
Later we have the disconnect method created to close the connection and the receive method that returns by default the data sent from the client to itself.
import jsonimport timefrom channels.generic.websocket import WebsocketConsumerfrom socketapp import modelsclass Consumer(WebsocketConsumer):def connect(self):self.accept()while int(1) in Queue.objects.all().values_list('status', flat=True):self.send(text_data=json.dumps({"value":Random.objects.all().count()}))Random.objects.create(text = "test")time.sleep(2)self.close()def disconnect(self, close_code):passdef receive(self, text_data):self.send(text_data=text_data)self.close()import json import time from channels.generic.websocket import WebsocketConsumer from socketapp import models class Consumer(WebsocketConsumer): def connect(self): self.accept() while int(1) in Queue.objects.all().values_list('status', flat=True): self.send(text_data=json.dumps({"value":Random.objects.all().count()})) Random.objects.create(text = "test") time.sleep(2) self.close() def disconnect(self, close_code): pass def receive(self, text_data): self.send(text_data=text_data) self.close()import json import time from channels.generic.websocket import WebsocketConsumer from socketapp import models class Consumer(WebsocketConsumer): def connect(self): self.accept() while int(1) in Queue.objects.all().values_list('status', flat=True): self.send(text_data=json.dumps({"value":Random.objects.all().count()})) Random.objects.create(text = "test") time.sleep(2) self.close() def disconnect(self, close_code): pass def receive(self, text_data): self.send(text_data=text_data) self.close()
Enter fullscreen mode Exit fullscreen mode
Any logic could be implemented to customize the disconnect and receive methods.
- Now let’s create our routes file. He will be responsible for making our consumer logic available to consumers.
We will create another file called routing.py. This file is similar to a traditional url file from a common django project, but it uses another communication protocol.
from django.urls import pathfrom . import consumersws_urlpatterns = [path("ws/test/", consumers.Consumer.as_asgi())]from django.urls import path from . import consumers ws_urlpatterns = [ path("ws/test/", consumers.Consumer.as_asgi()) ]from django.urls import path from . import consumers ws_urlpatterns = [ path("ws/test/", consumers.Consumer.as_asgi()) ]
Enter fullscreen mode Exit fullscreen mode
Explaining this code, we use the path method to create our routes, this method will connect our logic created in the Consumer class and relate it to a url defined in the first parameter of the method.
Note that we use a method called “as_asgi” in the Consumer class. This method allows our class to be managed by our Daphne as an ASGI object.
- Now you remember that previously we left it to configure our ASGI later? Well, now is the time.Here we configure the ASGI entry point for our Django application.
import osfrom django.core.asgi import get_asgi_applicationfrom channels.routing import ProtocolTypeRouter, URLRouterfrom channels.auth import AuthMiddlewareStackfrom socketapp import routingos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'socketdevto.settings')application = ProtocolTypeRouter({"http": get_asgi_application(),"websocket": AuthMiddlewareStack(URLRouter(routing.ws_urlpatterns)),})import os from django.core.asgi import get_asgi_application from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack from socketapp import routing os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'socketdevto.settings') application = ProtocolTypeRouter({ "http": get_asgi_application(), "websocket": AuthMiddlewareStack( URLRouter( routing.ws_urlpatterns ) ), })import os from django.core.asgi import get_asgi_application from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack from socketapp import routing os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'socketdevto.settings') application = ProtocolTypeRouter({ "http": get_asgi_application(), "websocket": AuthMiddlewareStack( URLRouter( routing.ws_urlpatterns ) ), })
Enter fullscreen mode Exit fullscreen mode
Step-by-step
In this line we inform ASGI which file it will pull the settings from, that is, we point out the environment variables.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'socketdevto.settings')
After that, with ‘ProtocolTypeRouter’ we will define how each protocol will be treated in our application. In this case we are configuring the HTTP protocol and the WebSocket protocol.
"http": get_asgi_application()
For the HTTP protocol we will use django’s default ASGI application.
"websocket": AuthMiddlewareStack(
URLRouter(
routing.ws_urlpatterns
)
),
For the WebSocket protocol we will use a stack of middleware (functions that intermediate communication, changing behaviors, adding or removing information) similar to the standard middleware present in our project’s settings.py file. Next we call the URL router to map our WebSockets endpoints and our urls.
Note that we are passing our small list of urls defined in the ‘routing.py’ file as a parameter.
Seeing the result of our implementation.
- We will start by building our html file to display our data in the browser.
<html><head><title>Teste</title></head><body><h1 id = "app">{{ value }}</h1></body></html><script>var socket = new WebSocket("ws://localhost:8000/ws/test/");socket.onmessage = function(event) {var djangodata = event.data;console.log(djangodata);document.querySelector("#app").innerText = djangodata;};</script><html> <head> <title>Teste</title> </head> <body> <h1 id = "app">{{ value }}</h1> </body> </html> <script> var socket = new WebSocket("ws://localhost:8000/ws/test/"); socket.onmessage = function(event) { var djangodata = event.data; console.log(djangodata); document.querySelector("#app").innerText = djangodata; }; </script><html> <head> <title>Teste</title> </head> <body> <h1 id = "app">{{ value }}</h1> </body> </html> <script> var socket = new WebSocket("ws://localhost:8000/ws/test/"); socket.onmessage = function(event) { var djangodata = event.data; console.log(djangodata); document.querySelector("#app").innerText = djangodata; }; </script>
Enter fullscreen mode Exit fullscreen mode
file name: test.html
Let me explain our JavaScript code. With this code we create ‘the other part’ of two-way communication, the listener.
This JavaScript code establishes a WebSocket connection with a server, listens for incoming messages, logs the message content to the browser console, and updates an HTML element with the received message content.
- Now we will create our view, which will render our data in the browser.
#views.pyfrom socketapp import modelsfrom django.shortcuts import renderdef test(request):return render(request, 'test.html', context = {})#views.py from socketapp import models from django.shortcuts import render def test(request): return render(request, 'test.html', context = {})#views.py from socketapp import models from django.shortcuts import render def test(request): return render(request, 'test.html', context = {})
Enter fullscreen mode Exit fullscreen mode
- Now let’s access the django shell, import our models and create a Queue object with status = 1.
python3 manage.py shellfrom yourappname import modelsmodels.Queue.objects.create(status = 1)exit()python3 manage.py shell from yourappname import models models.Queue.objects.create(status = 1) exit()python3 manage.py shell from yourappname import models models.Queue.objects.create(status = 1) exit()
Enter fullscreen mode Exit fullscreen mode
- Finally you can run the command
python3 manage.py runserver
and access the following address on your machine:http://127.0.0.1:8000/test
. You should see a dictionary like this {“value”: int}, where the value of the “value” key grows in 2 second intervals without having to refresh your browser.
You can check out the entire project here
原文链接:Sockets for real-time data flow with Django Rest Framework
暂无评论内容