DEV Community

Cover image for Sockets for real-time data flow with Django Rest Framework
Alisson
Alisson

Posted on • Edited on

Sockets for real-time data flow with Django Rest Framework

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:

  1. Create a project directory named 'sockets':
   mkdir sockets
   cd sockets
Enter fullscreen mode Exit fullscreen mode
  1. Update your package manager 'pip':
   python3.10 -m pip install --upgrade pip
Enter fullscreen mode Exit fullscreen mode
  1. Install the Django framework and Daphne:
   pip 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

  1. Create and activate your virtual environment.
python3.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.

  1. Create your django project and your django app with these commands:
django-admin startproject yourprojectname
django-admin startapp yourappname
Enter fullscreen mode Exit fullscreen mode
  1. 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 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
  1. 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'

Enter fullscreen mode Exit fullscreen mode

We will configure our ASGI server later. 😄

*Now let's configure our consumers, routing and models files.

  1. Firstly, let's just create a model to serve data for our socket.
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 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.

  1. 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 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.

  1. 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 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.

  1. 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 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.

  1. 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>
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.

  1. Now we will create our view, which will render our data in the browser.
#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
  1. Now let's access the django shell, import our models and create a Queue object with status = 1.
python3 manage.py shell
from yourappname import models

models.Queue.objects.create(status = 1)

exit()
Enter fullscreen mode Exit fullscreen mode
  1. 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

Top comments (0)