DEV Community

Israjur Rahman
Israjur Rahman

Posted on

How to implement asynchronous socket status using django channels

While developing an analytical project using django we came across an issue where we have to show status when our users start analysis. We decided to implement this using django channels. There are many tutorials about django channels on the internet but all of them are a little complex or using django channels to build a chat application. So I decided to write this article where I will show how to send asynchronous status to users using django channels.

I am assuming you know at least the basics of django framework and how django works, so I am not going to show how to create a django project and django apps. I will directly start from implementing django channels.

First we will start a project channelproj and create an app notifier.

We need to install channels and channels-redis using pip :

pip install channels channels-redis
Enter fullscreen mode Exit fullscreen mode

We need to install redis-server to out system:

sudo apt update
sudo apt install redis-server
Enter fullscreen mode Exit fullscreen mode

Check redis-server status:

sudo systemctl status redis
Enter fullscreen mode Exit fullscreen mode

Redis Active Status

Here we can see redis server is active and running on port 6379

Then open django channelproj/settings.py file and add channels and notifier to INSTALLED_APPS.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels', # Here we added channels 
    'notifier' # we will use django channels in this app
]
Enter fullscreen mode Exit fullscreen mode

We also need to add this lines in settings.py file:

ASGI_APPLICATION = 'channelproj.asgi.application'

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],
        },
    },
}
Enter fullscreen mode Exit fullscreen mode

To know more about channel layer click here

Now we will create a file notifier/consumers.py and add the following class

from channels.generic.websocket import AsyncJsonWebsocketConsumer

class StatusConsumer(AsyncJsonWebsocketConsumer):
    room_group_name = 'notify'

    async def connect(self):
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )
        await self.accept()

    async def disconnect(self, code):
        await self.channel_layer.group_discard(
            self.room_group_name, 
            self.channel_layer
        )

    async def status_notifier(self, event):
        await self.send_json(event)
Enter fullscreen mode Exit fullscreen mode

Here we are using AsyncJsonWebsocketConsumer and creating a group named notify and we have defined a function called status_notifier which will send all the events via socket url as json format.

We will create another file notifier/routing.py and add our socket urls

from django.urls import path

from .consumers import StatusConsumer

ws_urlpatterns = [
    path('ws/status/', StatusConsumer.as_asgi())
]
Enter fullscreen mode Exit fullscreen mode

Now open channelproj/asgi.py file and edit the file as below:

from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application

from notifier.routing import ws_urlpatterns

os.environ.setdefault('DJANGO_SETTINGS_MODULE',
'channelproj.settings')

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": URLRouter(
        ws_urlpatterns
    )
})
Enter fullscreen mode Exit fullscreen mode

Here we have imported ws_urlpatterns from notifier/routing.py added ProtocolTypeRouter and defined our websocket urls using URLRouter

Now we will create a template notifier/templates/home.html in our notifier app where we will add a form with an input field.

  {% load static %}
  <html>
    <head>
      <title>Django Channels Status</title>
    </head>
    <body>
      <h1>Check Status</h1>

    <form action="{% url 'staus' %}" method="post">
      {% csrf_token %}
      <input type="text" placeholder="enter anything" name="TB_sample" id="TB_sample"/><br>
      <input type="submit" value="submit">
    </form>

  </body>
  </html>
Enter fullscreen mode Exit fullscreen mode

Now we will add a two function in notifier/views.py

from django.shortcuts import render, redirect
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
import time


def index(request):
    return render(request, 'home.html')

def status_form(request):
    if request.method =='POST':
        num = int(request.POST['TB_sample'])
        progress = 10
        for i in range(num):
            room_group_name = f'notify'
            channel_layer = get_channel_layer()
            async_to_sync(channel_layer.group_send)(
                room_group_name, {
                    "type": "status.notifier",
                    "data": progress
                }
            )
            message = "Status Running"
            progress += 10
            time.sleep(1)

    context = {'message': message}
    return render(request, 'home.html', context)
Enter fullscreen mode Exit fullscreen mode

Here we have defined a function status_form which will be called when a post request happens, it will get the post value in num variable and we have declared a variable called progress with value 10, we have defined a loop which will run and add 10 every time loop runs to progress and sleep for 1 second. We have added channel_layer = get_channel_layer() line to get the channel layer and async_to_sync method to send the data asynchronously to room_group_name.

Note: Here by declaring "type": "status.notifier" we are calling status_notifier function we wrote in notifier/consumers.py file.

Also edit channelproj/urls.py file like this:

from notifier.views import index,status_form

urlpatterns = [
    path('', index),
    path('status/', status_form, name="status"),
    path('admin/', admin.site.urls),
]
Enter fullscreen mode Exit fullscreen mode

Now if we run the project using python manage.py runserver and open http://127.0.0.1:8000/ url in our browser it will look something like this

Alt Text

To check socket status we will use a chrome extension called Simple WebSocket Client. Add the extension and click the extension icon in chrome and insert our web socket url ws://127.0.0.1:8000/ws/status/ and click open, connection should be established.

Now go to project tab and insert 10 in input filed and submit the form and move to Simple WebSocket Client tab. We will see the status we are sending to socket appearing every second until the loop completes.

Socket Status

I have uploaded a sample project to github you can get here.

Thank you for reading!

Top comments (0)