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-redispip install channels channels-redispip install channels channels-redis
Enter fullscreen mode Exit fullscreen mode
We need to install redis-server to out system:
sudo apt updatesudo apt install redis-serversudo apt update sudo apt install redis-serversudo apt update sudo apt install redis-server
Enter fullscreen mode Exit fullscreen mode
Check redis-server status:
sudo systemctl status redissudo systemctl status redissudo systemctl status redis
Enter fullscreen mode Exit fullscreen mode
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]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 ]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)],},},}ASGI_APPLICATION = 'channelproj.asgi.application' CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [("127.0.0.1", 6379)], }, }, }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 AsyncJsonWebsocketConsumerclass 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)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)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 pathfrom .consumers import StatusConsumerws_urlpatterns = [path('ws/status/', StatusConsumer.as_asgi())]from django.urls import path from .consumers import StatusConsumer ws_urlpatterns = [ path('ws/status/', StatusConsumer.as_asgi()) ]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, URLRouterfrom django.core.asgi import get_asgi_applicationfrom notifier.routing import ws_urlpatternsos.environ.setdefault('DJANGO_SETTINGS_MODULE','channelproj.settings')application = ProtocolTypeRouter({"http": get_asgi_application(),"websocket": URLRouter(ws_urlpatterns)})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 ) })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>{% 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>{% 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, redirectfrom asgiref.sync import async_to_syncfrom channels.layers import get_channel_layerimport timedef index(request):return render(request, 'home.html')def status_form(request):if request.method =='POST':num = int(request.POST['TB_sample'])progress = 10for 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 += 10time.sleep(1)context = {'message': message}return render(request, 'home.html', context)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)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_formurlpatterns = [path('', index),path('status/', status_form, name="status"),path('admin/', admin.site.urls),]from notifier.views import index,status_form urlpatterns = [ path('', index), path('status/', status_form, name="status"), path('admin/', admin.site.urls), ]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
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.
I have uploaded a sample project to github you can get here.
Thank you for reading!
原文链接:How to implement asynchronous socket status using django channels
暂无评论内容