python

Realtime chat app using Django Daphne Nginx Redis

· John Doe

940 Views

Create your virtual environment.

download package in this order

Django==3.0.8
djangorestframework==3.11.0
websocket-client==0.57.0
redis==3.5.3
asgiref==3.2.10
channels-redis==2.4.2
channels==3.0.1

Then create a Django project named ChatApp.

django-admin startproject ChatApp

After installing channels, add channels to your installed apps.

INSTALLED_APPS = [
	'chat.apps.ChatConfig',
	
	'django.contrib.admin',
	'django.contrib.auth',
	'django.contrib.contenttypes',
	'django.contrib.sessions',
	'django.contrib.messages',
	'django.contrib.staticfiles',
	
	# add django channels
	'channels' ,
]

Set the ASGI application to your default ASGI file in the project.

ASGI_APPLICATION = 'ChatApp.asgi.application'

Create a new app that will have all the chat functionality.

python manage.py startapp chat

And add your app to the installed apps in settings.py.

And add chat/urls.py

from django.urls import path, include
from chat import views as chat_views

urlpatterns = [
    path("chat", chat_views.chatPage, name="chat-page"),
]

And add chat/routing.py

from django.urls import re_path
from chat.consumers import ChatConsumer

# Here, "ws" is routing to the URL ChatConsumer which
# will handle the chat functionality.
websocket_urlpatterns = [
    re_path(r'ws$', ChatConsumer.as_asgi()),
]

And add chat/consumers.py

import json
from channels.generic.websocket import AsyncWebsocketConsumer

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.roomGroupName = "group_chat_gfg"
        await self.channel_layer.group_add(
            self.roomGroupName,
            self.channel_name
        )
        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.roomGroupName,
            self.channel_name
        )

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json["message"]
        username = text_data_json["username"]
        await self.channel_layer.group_send(
            self.roomGroupName, {
                "type": "sendMessage",
                "message": message,
                "username": username,
            })

    async def sendMessage(self, event):
        message = event["message"]
        username = event["username"]
        await self.send(text_data=json.dumps({"message": message, "username": username}))

And add ChatApp/asgi.py

* Has anyone had problem like this?

Traceback (most recent call last):

File "/path/to/my/env/bin/daphne", line 11, in <module>
    sys.exit(CommandLineInterface.entrypoint())
  File "/path/to/my/env/lib/python3.6/site-packages/daphne/cli.py", line 161, in entrypoint
    cls().run(sys.argv[1:])
  File "/path/to/my/env/lib/python3.6/site-packages/daphne/cli.py", line 222, in run
    application = import_by_path(args.application)
  File "/path/to/my/env/lib/python3.6/site-packages/daphne/utils.py", line 12, in import_by_path
    target = importlib.import_module(module_path)
  File "/path/to/my/env/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "./my_project/asgi.py", line 5, in <module>
    application = get_default_application()
  File "/path/to/my/env/lib/python3.6/site-packages/channels/routing.py", line 33, in get_default_application
    module = importlib.import_module(path)
  File "/path/to/my/env/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "./my_project/routing.py", line 4, in <module>
    from channels.auth import AuthMiddlewareStack
  File "/path/to/my/env/lib/python3.6/site-packages/channels/auth.py", line 12, in <module>
    from django.contrib.auth.models import AnonymousUser
  File "/path/to/my/env/lib/python3.6/site-packages/django/contrib/auth/models.py", line 2, in <module>
    from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
  File "/path/to/my/env/lib/python3.6/site-packages/django/contrib/auth/base_user.py", line 47, in <module>
    class AbstractBaseUser(models.Model):
  File "/path/to/my/env/lib/python3.6/site-packages/django/db/models/base.py", line 100, in __new__
    app_config = apps.get_containing_app_config(module)
  File "/path/to/my/env/lib/python3.6/site-packages/django/apps/registry.py", line 244, in get_containing_app_config
    self.check_apps_ready()
  File "/path/to/my/env/lib/python3.6/site-packages/django/apps/registry.py", line 127, in check_apps_ready
    raise AppRegistryNotReady("Apps aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.

Then visit this page.

I'm tring to run my django project with usage of asgi instead of wsgi. I have set up my routing.py and asgi.py as follows: routing.py from django.conf.urls import url from channels.routing import

 

ChatApp/settings.py

CHANNEL_LAYERS = {
	"default": {
		"BACKEND": "channels.layers.InMemoryChannelLayer"
	}
}

 

Using Redis:

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

 

* Deploying Django Channels: how to keep Daphne running after exiting shell on web server

As practice, I'm trying to deploy Andrew Godwin's multichat example with Django Channels 2.1.1 on DigitalOcean Ubuntu 16.04.4. However, I don't know how to exit the Ubuntu server without Channels' ...

 

Nginx

1. Windows

location @django {
	proxy_pass http://127.0.0.1:1234;
	proxy_set_header X-Real-IP  $remote_addr;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header X-Forwarded-Proto $scheme;
	proxy_read_timeout 600s;

	# this next line adds the Host header so that apache knows which vHost to serve
	# the $host variable is automatically set to the hostname Nginx is responding to

	proxy_set_header Host $host;
	
	#Websocket support
	proxy_http_version 1.1;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection "upgrade";
}


2. Linux with Daphne

Make service-name.service

[Unit]
Description=Indifference Daphne Service
After=network.target

[Service]
Type=simple
User=indifference
WorkingDirectory=/home/indifference/path/to/indifference
ExecStart=/home/indifference/path/to/bin/daphne -p 3333 indifference.asgi:application
access-log=/data/logs/indifference/daphne/access.log

[Install]
WantedBy=multi-user.target
chmod 755 service-name.service
systemctl daemon-reload
systemctl enable service-name.service
systemctl start service-name.service

Update nginx.conf 

upstream channels-indifference-backend {
   server localhost:3333;
}

...

location /ws {
   proxy_pass http://channels-indifference-backend;
   proxy_http_version 1.1;
   proxy_set_header Upgrade $http_upgrade;
   proxy_set_header Connection "upgrade";
   proxy_redirect off;
   proxy_set_header Host $host;
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   proxy_set_header X-Forwarded-Host $server_name;
}

 

Windows Django - dev

Create a folder called config

config/
    commonsettings.py
    dev.py
    prod.py

make sure that in dev.py and prod.py you import everything from commonsettings.py like this:

from .commonsettings import * 

dev.py sample

INSTALLED_APPS = [
  ...
  # 'channels',
  ...
]

ASGI_APPLICATION = None

then if you want to run the dev.py settings:

python manage.py runserver --settings=config.dev

In order to run your asgi application, simply point Daphne to your ASGI application, and optionally set a bind address and port (defaults to localhost, port 8000):

daphne -b 0.0.0.0 -p 9001 myproject.asgi:application

Nginx WS config is the same with 2. Linux with Daphne

* Use ASGI to deploy Django, StreamingHttpResponse cannot be accessed.

Async support for StreamingHttpResponse was only added in Django 4.2.

 

You can check the program with this:

https://www.phenomena.com/chat

 

Ref.

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.
I am writing a custom authentication middleware for django channels class TokenAuthMiddleware: def __init__(self, inner): # Store the ASGI application we were passed self.inner =

 

django daphne nginx redis