├── ChatApp ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── tests.py ├── apps.py ├── routing.py ├── urls.py ├── admin.py ├── models.py ├── views.py └── consumers.py ├── ChatProject ├── __init__.py ├── wsgi.py ├── asgi.py ├── urls.py └── settings.py ├── db.sqlite3 ├── requirements.txt ├── manage.py ├── templates ├── index.html ├── _message.html └── message.html ├── chatapp templates ├── index.html └── _message.html └── readme.md /ChatApp/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ChatProject/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ChatApp/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ChatApp/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheProtonGuy/DjangoChannelsChatApp/HEAD/db.sqlite3 -------------------------------------------------------------------------------- /ChatApp/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ChatappConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'ChatApp' 7 | -------------------------------------------------------------------------------- /ChatApp/routing.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .consumers import ChatConsumer 3 | 4 | websocket_urlpatterns = [ 5 | path('ws/notification//', ChatConsumer.as_asgi()), 6 | ] -------------------------------------------------------------------------------- /ChatApp/urls.py: -------------------------------------------------------------------------------- 1 | from . import views 2 | from django.urls import path 3 | 4 | urlpatterns = [ 5 | path('', views.CreateRoom, name='create-room'), 6 | path('//', views.MessageView, name='room'), 7 | ] -------------------------------------------------------------------------------- /ChatApp/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import * 3 | 4 | admin.site.register(Room) 5 | 6 | class MessageAdmin(admin.ModelAdmin): 7 | list_display = ['room', 'sender', 'message'] 8 | 9 | admin.site.register(Message, MessageAdmin) -------------------------------------------------------------------------------- /ChatProject/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for ChatProject project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ChatProject.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.7.2 2 | attrs==23.1.0 3 | autobahn==23.1.2 4 | Automat==22.10.0 5 | certifi==2023.7.22 6 | cffi==1.15.1 7 | channels==4.0.0 8 | charset-normalizer==3.2.0 9 | constantly==15.1.0 10 | cryptography==41.0.3 11 | daphne==4.0.0 12 | Django==4.2.5 13 | django-channels==0.7.0 14 | hyperlink==21.0.0 15 | idna==3.4 16 | incremental==22.10.0 17 | oauthlib==3.2.2 18 | pyasn1==0.5.0 19 | pyasn1-modules==0.3.0 20 | pycparser==2.21 21 | pyOpenSSL==23.2.0 22 | requests==2.31.0 23 | requests-oauthlib==1.3.1 24 | service-identity==23.1.0 25 | six==1.16.0 26 | sqlparse==0.4.4 27 | Twisted==23.8.0 28 | txaio==23.1.1 29 | typing_extensions==4.7.1 30 | urllib3==2.0.4 31 | zope.interface==6.0 32 | -------------------------------------------------------------------------------- /ChatApp/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Room(models.Model): 4 | room_name = models.CharField(max_length=255) 5 | 6 | def __str__(self): 7 | return self.room_name 8 | 9 | def return_room_messages(self): 10 | 11 | return Message.objects.filter(room=self) 12 | 13 | def create_new_room_message(self, sender, message): 14 | 15 | new_message = Message(room=self, sender=sender, message=message) 16 | new_message.save() 17 | 18 | class Message(models.Model): 19 | room = models.ForeignKey(Room, on_delete=models.CASCADE) 20 | sender = models.CharField(max_length=255) 21 | message = models.TextField() 22 | 23 | def __str__(self): 24 | return str(self.room) 25 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ChatProject.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /ChatProject/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for ChatProject project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | from channels.routing import ProtocolTypeRouter, URLRouter 14 | from django.core.asgi import get_asgi_application 15 | from ChatApp import routing 16 | 17 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ChatProject.settings') 18 | 19 | django_asgi_app = get_asgi_application() 20 | 21 | application = ProtocolTypeRouter({ 22 | "http": django_asgi_app, 23 | "websocket": URLRouter( 24 | routing.websocket_urlpatterns 25 | ) 26 | }) 27 | -------------------------------------------------------------------------------- /ChatProject/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for ChatProject project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.contrib import admin 18 | from django.urls import path, include 19 | 20 | urlpatterns = [ 21 | path('admin/', admin.site.urls), 22 | path('', include('ChatApp.urls')) 23 | ] 24 | -------------------------------------------------------------------------------- /ChatApp/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.5 on 2023-09-09 10:10 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Room', 17 | fields=[ 18 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('room_name', models.CharField(max_length=255)), 20 | ], 21 | ), 22 | migrations.CreateModel( 23 | name='Message', 24 | fields=[ 25 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 26 | ('sender', models.CharField(max_length=255)), 27 | ('message', models.TextField()), 28 | ('room', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ChatApp.room')), 29 | ], 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /ChatApp/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from .models import * 3 | 4 | def CreateRoom(request): 5 | 6 | if request.method == 'POST': 7 | username = request.POST['username'] 8 | room = request.POST['room'] 9 | 10 | try: 11 | get_room = Room.objects.get(room_name=room) 12 | return redirect('room', room_name=room, username=username) 13 | 14 | except Room.DoesNotExist: 15 | new_room = Room(room_name = room) 16 | new_room.save() 17 | return redirect('room', room_name=room, username=username) 18 | 19 | return render(request, 'index.html') 20 | 21 | def MessageView(request, room_name, username): 22 | 23 | get_room = Room.objects.get(room_name=room_name) 24 | 25 | if request.method == 'POST': 26 | message = request.POST['message'] 27 | 28 | print(message) 29 | 30 | new_message = Message(room=get_room, sender=username, message=message) 31 | new_message.save() 32 | 33 | get_messages= Message.objects.filter(room=get_room) 34 | 35 | context = { 36 | "messages": get_messages, 37 | "user": username, 38 | "room_name": room_name, 39 | } 40 | return render(request, 'message.html', context) -------------------------------------------------------------------------------- /ChatApp/consumers.py: -------------------------------------------------------------------------------- 1 | import json 2 | from channels.generic.websocket import AsyncWebsocketConsumer 3 | from channels.db import database_sync_to_async 4 | from ChatApp.models import * 5 | 6 | class ChatConsumer(AsyncWebsocketConsumer): 7 | async def connect(self): 8 | self.room_name = f"room_{self.scope['url_route']['kwargs']['room_name']}" 9 | await self.channel_layer.group_add(self.room_name, self.channel_name) 10 | await self.accept() 11 | 12 | async def disconnect(self, close_code): 13 | await self.channel_layer.group_discard(self.room_name, self.channel_name) 14 | 15 | async def receive(self, text_data): 16 | text_data_json = json.loads(text_data) 17 | message = text_data_json 18 | 19 | event = { 20 | 'type': 'send_message', 21 | 'message': message, 22 | } 23 | 24 | await self.channel_layer.group_send(self.room_name, event) 25 | 26 | async def send_message(self, event): 27 | 28 | data = event['message'] 29 | await self.create_message(data=data) 30 | 31 | response_data = { 32 | 'sender': data['sender'], 33 | 'message': data['message'] 34 | } 35 | await self.send(text_data=json.dumps({'message': response_data})) 36 | 37 | @database_sync_to_async 38 | def create_message(self, data): 39 | 40 | get_room_by_name = Room.objects.get(room_name=data['room_name']) 41 | 42 | if not Message.objects.filter(message=data['message']).exists(): 43 | new_message = Message(room=get_room_by_name, sender=data['sender'], message=data['message']) 44 | new_message.save() 45 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Enter Room 8 | 9 | 10 |
11 |
12 |

Enter Room

13 |
14 | {% csrf_token %} 15 |
16 |
17 |
18 |
19 |

20 |
21 |
22 |
23 | 24 | 79 | -------------------------------------------------------------------------------- /chatapp templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Enter Room 8 | 9 | 10 |
11 |
12 |

Enter Room

13 |
14 | {% csrf_token %} 15 |
16 |
17 |
18 |
19 |

20 |
21 |
22 |
23 | 24 | 79 | -------------------------------------------------------------------------------- /ChatProject/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for ChatProject project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.2.1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'django-insecure-=m1^$ql(%@l&m_d()&st30d!b^hc5cjl5@9!+*mlheo)v&o(y=' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'daphne', 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | 'ChatApp', 42 | 'channels', 43 | ] 44 | 45 | MIDDLEWARE = [ 46 | 'django.middleware.security.SecurityMiddleware', 47 | 'django.contrib.sessions.middleware.SessionMiddleware', 48 | 'django.middleware.common.CommonMiddleware', 49 | 'django.middleware.csrf.CsrfViewMiddleware', 50 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 51 | 'django.contrib.messages.middleware.MessageMiddleware', 52 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 53 | ] 54 | 55 | ROOT_URLCONF = 'ChatProject.urls' 56 | 57 | TEMPLATES = [ 58 | { 59 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 60 | 'DIRS': ['templates'], 61 | 'APP_DIRS': True, 62 | 'OPTIONS': { 63 | 'context_processors': [ 64 | 'django.template.context_processors.debug', 65 | 'django.template.context_processors.request', 66 | 'django.contrib.auth.context_processors.auth', 67 | 'django.contrib.messages.context_processors.messages', 68 | ], 69 | }, 70 | }, 71 | ] 72 | 73 | WSGI_APPLICATION = 'ChatProject.wsgi.application' 74 | ASGI_APPLICATION = "ChatProject.asgi.application" 75 | CHANNEL_LAYERS = { 76 | 77 | "default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}, 78 | } 79 | 80 | 81 | # Database 82 | # https://docs.djangoproject.com/en/4.2/ref/settings/#databases 83 | 84 | DATABASES = { 85 | 'default': { 86 | 'ENGINE': 'django.db.backends.sqlite3', 87 | 'NAME': BASE_DIR / 'db.sqlite3', 88 | } 89 | } 90 | 91 | 92 | # Password validation 93 | # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators 94 | 95 | AUTH_PASSWORD_VALIDATORS = [ 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 101 | }, 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 104 | }, 105 | { 106 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 107 | }, 108 | ] 109 | 110 | 111 | # Internationalization 112 | # https://docs.djangoproject.com/en/4.2/topics/i18n/ 113 | 114 | LANGUAGE_CODE = 'en-us' 115 | 116 | TIME_ZONE = 'UTC' 117 | 118 | USE_I18N = True 119 | 120 | USE_TZ = True 121 | 122 | 123 | # Static files (CSS, JavaScript, Images) 124 | # https://docs.djangoproject.com/en/4.2/howto/static-files/ 125 | 126 | STATIC_URL = 'static/' 127 | 128 | # Default primary key field type 129 | # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field 130 | 131 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 132 | -------------------------------------------------------------------------------- /templates/_message.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Messages 9 | 10 | {% load static %} 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

Chats


19 |
20 |
21 |
22 | 23 | 24 | 25 |
26 |

{{i.message}}-{{i.sender}}

27 |
28 | 29 |
30 |

{{i.message}}

31 |
32 | 33 | 34 |
35 | 36 |
37 |
38 | {% csrf_token %} 39 | 40 | 41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 | 56 | 245 | -------------------------------------------------------------------------------- /chatapp templates/_message.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Messages 9 | 10 | {% load static %} 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

Chats


19 |
20 |
21 |
22 | 23 | 24 | 25 |
26 |

{{i.message}}-{{i.sender}}

27 |
28 | 29 |
30 |

{{i.message}}

31 |
32 | 33 | 34 |
35 | 36 |
37 |
38 | {% csrf_token %} 39 | 40 | 41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 | 56 | 245 | -------------------------------------------------------------------------------- /templates/message.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Messages 9 | 10 | {% load static %} 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

Chats


19 |
20 |
21 |
22 | 23 | {% for i in messages %} 24 | {% if i.sender != user %} 25 |
26 |

{{i.message}}-{{i.sender}}

27 |
28 | {% else %} 29 |
30 |

{{i.message}}

31 |
32 | {% endif %} 33 | {% endfor %} 34 | 35 |
36 | 37 |
38 |
39 | {% csrf_token %} 40 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 | 49 | 104 | 293 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Chat Website with Django and Django Channels 2 | 3 | This tutorial will guide you through creating a chat website using Django and Django Channels. 4 | 5 | ## Getting Started 6 | 7 | ### 1. Setting up a Django Project 8 | 9 | 1. Create and enter the desired directory for project setup. 10 | 11 | 2. Create a virtual environment using pipenv or other means: 12 | 13 | ```shell 14 | pipenv shell 15 | ``` 16 | 17 | 3. Install Django: 18 | 19 | ```shell 20 | pip install django 21 | ``` 22 | 23 | 4. Create a Django project called ChatPrj: 24 | 25 | ```shell 26 | django-admin startproject ChatPrj 27 | ``` 28 | 29 | 5. Create an app called ChatApp: 30 | 31 | ```shell 32 | python manage.py startapp ChatApp 33 | ``` 34 | 35 | 6. Open the project in your code editor. 36 | 37 | 7. Create a templates folder and register it in the project's settings. 38 | 39 | 8. Register the app in the project's settings. 40 | 41 | 9. Create URLs for the app and register them in the project's URLs. 42 | 43 | ### 2. Installing Libraries 44 | 45 | 1. Install Django Channels: 46 | 47 | ```shell 48 | pip install django-channels 49 | ``` 50 | 51 | 2. Install Daphne: 52 | 53 | ```shell 54 | pip install daphne 55 | ``` 56 | 3. Add ChatApp, daphne and channels to `installed_apps` in `settings.py` file: 57 | ```python 58 | INSTALLED_APPS = [ 59 | 'daphne', 60 | 'django.contrib.admin', 61 | 'django.contrib.auth', 62 | 'django.contrib.contenttypes', 63 | 'django.contrib.sessions', 64 | 'django.contrib.messages', 65 | 'django.contrib.staticfiles', 66 | 'ChatApp', 67 | 'channels', 68 | ] 69 | ``` 70 | ### 3. Create Important Files in the App Folder 71 | 72 | 1. Create `routing.py`. 73 | 74 | 2. Create `consumers.py`. 75 | 76 | ### 4. Creating Models 77 | 78 | 1. Create a model called `Room`: 79 | 80 | ```python 81 | class Room(models.Model): 82 | room_name = models.CharField(max_length=255) 83 | 84 | def __str(self): 85 | return self.room_name 86 | ``` 87 | 88 | 2. Create another model called `Message`: 89 | 90 | ```python 91 | class Message(models.Model): 92 | room = models.ForeignKey(Room, on_delete=models.CASCADE) 93 | sender = models.CharField(max_length=255) 94 | message = models.TextField() 95 | 96 | def __str(self): 97 | return str(self.room) 98 | ``` 99 | 100 | 3. Make migrations and migrate: 101 | 102 | ```shell 103 | python manage.py makemigrations 104 | python manage.py migrate 105 | ``` 106 | 107 | 4. Register the models in the `admin.py` file: 108 | 109 | ```python 110 | from .models import * 111 | 112 | admin.site.register(Room) 113 | admin.site.register(Message) 114 | ``` 115 | 116 | 117 | # 5. Getting Template Files from GitHub 118 | 119 | - Download the following HTML templates from GitHub: 120 | - `index.html` 121 | - `message.html` 122 | 123 | 124 | 125 | ### 6. Create Views 126 | 127 | 1. CreateRoom view: 128 | 129 | ```python 130 | def CreateRoom(request): 131 | return render(request, 'index.html') 132 | ``` 133 | 134 | 2. Message view: 135 | 136 | ```python 137 | def MessageView(request, room_name, username): 138 | return render(request, 'message.html') 139 | ``` 140 | 141 | ### 7. Map Views to URLs: 142 | 143 | ```python 144 | from . import views 145 | from django.urls import path 146 | 147 | urlpatterns = [ 148 | path('', views.CreateRoom, name='create-room'), 149 | path('//', views.MessageView, name='room'), 150 | ] 151 | ``` 152 | 153 | ### 8. View CreateRoom view in browser to make sure setup works 154 | 155 | ### 9. Allow users to login or create chat rooms in CreateRoom view 156 | 157 | In your `index.html` file, make sure to include a CSRF token in form: 158 | 159 | ```html 160 | {% csrf_token %} 161 | ``` 162 | 163 | In your Django `CreateRoom` view, check for incoming POST requests: 164 | 165 | ```python 166 | if request.method == 'POST': 167 | ``` 168 | 169 | Retrieve user-entered data: 170 | 171 | ```python 172 | if request.method == 'POST': 173 | username = request.POST['username'] 174 | room = request.POST['room'] 175 | ``` 176 | 177 | Create try and except blocks to either get the room object or create it if it does not exist: 178 | 179 | ```python 180 | try: 181 | get_room = Room.objects.get(room_name=room) 182 | except Room.DoesNotExist: 183 | new_room = Room(room_name=room) 184 | new_room.save() 185 | ``` 186 | 187 | Test the code to see if it works. 188 | 189 | Next, redirect users to `MessageView`: 190 | 191 | ```python 192 | try: 193 | get_room = Room.objects.get(room_name=room) 194 | return redirect('room', room_name=room, username=username) 195 | except Room.DoesNotExist: 196 | new_room = Room(room_name=room) 197 | new_room.save() 198 | return redirect('room', room_name=room, username=username) 199 | ``` 200 | 201 | ### 10. Displaying Messages Created in a Room 202 | 203 | 1. Getting the room object and returning it as well as room name and username in the context 204 | 205 | ```python 206 | def MessageView(request, room_name, username): 207 | get_room = Room.objects.get(room_name=room_name) 208 | get_messages = Message.objects.filter(room=get_room) 209 | 210 | context = { 211 | "messages": get_messages, 212 | "user": username, 213 | "room_name": room_name, 214 | } 215 | 216 | return render(request, 'message.html', context) 217 | ``` 218 | 219 | 2. Display Messages from the Query Set in `message.html`: 220 | 221 | ```html 222 |
223 | 224 | {% for i in messages %} 225 | {% if i.sender != user %} 226 |
227 |

{{i.message}}-{{i.sender}}

228 |
229 | {% else %} 230 |
231 |

{{i.message}}

232 |
233 | {% endif %} 234 | {% endfor %} 235 | 236 |
237 | ``` 238 | 239 | This code is part of your `messages.html` file and is responsible for rendering the messages in the chat room. Messages are displayed differently based on whether the sender is the current user or another user. 240 | 241 | ### 11. Creating Consumers 242 | Head over to your `consumers.py` file 243 | 1. Importing Modules: 244 | 245 | ```python 246 | import json 247 | from channels.generic.websocket import AsyncWebsocketConsumer 248 | from channels.db import database_sync_to_async 249 | from ChatApp.models import * 250 | ``` 251 | 2. Creating `ChatConsumer`: 252 | 253 | ```python 254 | class ChatConsumer(AsyncWebsocketConsumer): 255 | ``` 256 | 257 | 3. Create `connect` Method: 258 | 259 | ```python 260 | class ChatConsumer(AsyncWebsocketConsumer): 261 | 262 | async def connect(self): 263 | self.room_name = f"room_{self.scope['url_route']['kwargs']['room_name']}" 264 | await self.channel_layer.group_add(self.room_name, self.channel_name) 265 | await self.accept() 266 | ``` 267 | 268 | 4. Create `disconnect` Method: 269 | 270 | ```python 271 | class ChatConsumer(AsyncWebsocketConsumer): 272 | 273 | async def connect(self): 274 | self.room_name = f"room_{self.scope['url_route']['kwargs']['room_name']}" 275 | await self.channel_layer.group_add(self.room_name, self.channel_name) 276 | await self.accept() 277 | 278 | async def disconnect(self, close_code): 279 | await self.channel_layer.group_discard(self.room_name, self.channel_name) 280 | ``` 281 | 282 | In this code section, you're creating a Django Channels consumer called `ChatConsumer`. It includes the `connect` method for WebSocket connection setup and the `disconnect` method for WebSocket disconnection handling. These consumers are essential for real-time communication in your Django application. 283 | 284 | ### 12. Creating URL for `ChatConsumer` 285 | 286 | Head to your `routing.py` File and Add the Following: 287 | 288 | ```python 289 | from django.urls import path 290 | from .consumers import ChatConsumer 291 | 292 | websocket_urlpatterns = [ 293 | path('ws/notification//', ChatConsumer.as_asgi()), 294 | ] 295 | ``` 296 | 297 | ### 13. Register routing url in `asgi.py` file in project 298 | Head to your `asgi.py` file in project folder 299 | 1. Importing Modules: 300 | 301 | ```python 302 | import os 303 | from django.core.asgi import get_asgi_application 304 | 305 | # imports 306 | from channels.routing import ProtocolTypeRouter, URLRouter 307 | from django.core.asgi import get_asgi_application 308 | from ChatApp import routing 309 | 310 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'movie.settings') 311 | 312 | application = get_asgi_application() 313 | ``` 314 | 315 | 2. Rename `application` to `django_asgi_app`: 316 | 317 | ```python 318 | django_asgi_app = get_asgi_application() 319 | ``` 320 | 321 | 3. Add the Following: 322 | 323 | ```python 324 | application = ProtocolTypeRouter({ 325 | "http": django_asgi_app, 326 | "websocket": URLRouter( 327 | routing.websocket_urlpatterns 328 | ) 329 | }) 330 | ``` 331 | 332 | 4. The Final Code: 333 | 334 | ```python 335 | import os 336 | from django.core.asgi import get_asgi_application 337 | 338 | # imports 339 | from channels.routing import ProtocolTypeRouter, URLRouter 340 | from django.core.asgi import get_asgi_application 341 | from ChatApp import routing 342 | 343 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'movie.settings') 344 | 345 | django_asgi_app = get_asgi_application() 346 | 347 | application = ProtocolTypeRouter({ 348 | "http": django_asgi_app, 349 | "websocket": URLRouter( 350 | routing.websocket_urlpatterns 351 | ) 352 | }) 353 | ``` 354 | 355 | ### 14. Adding `asgi.py` Configurations and `channel_layers` to Settings 356 | Head to `settings.py` file 357 | 1. Update `ASGI_APPLICATION` in your Settings: 358 | 359 | ```python 360 | ASGI_APPLICATION = "ChatProject.asgi.application" 361 | ``` 362 | 363 | 2. Add `channel_layers` Configuration: 364 | 365 | ```python 366 | CHANNEL_LAYERS = { 367 | "default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}, 368 | } 369 | ``` 370 | 371 | Here's the provided content in markdown format for your `readme.md` file: 372 | 373 | 374 | ### 15. Creating a New WebSocket 375 | 376 | 1. Head to `message.html` File and Create Script Tags: 377 | 378 | ```html 379 | 382 | ``` 383 | 384 | This step involves adding script tags to your `message.html` file to embed JavaScript code for handling WebSocket connections. 385 | 386 | 2. Create a New WebSocket: 387 | 388 | ```javascript 389 | 394 | ``` 395 | 396 | In this part of the code, you're creating a new WebSocket connection in your `message.html` file. It determines the WebSocket protocol based on whether the application is served over HTTPS or HTTP and establishes a connection to the WebSocket endpoint for the specific chat room. 397 | 398 | ### 16. Creating Event Handlers for WebSocket Connection 399 | 400 | Handling WebSocket Connection Events: 401 | 402 | ```javascript 403 | 421 | ``` 422 | 423 | ### 17. Creating an Event Listener for Sending Messages 424 | 425 | 426 | ```javascript 427 | 458 | ``` 459 | 460 | ### 18. Creating Methods in Consumers to Receive and Send New Messages 461 | 462 | 1. Creating a `ChatConsumer` with `receive` Method: 463 | 464 | ```python 465 | import json 466 | from channels.generic.websocket import AsyncWebsocketConsumer 467 | from channels.db import database_sync_to_async 468 | from ChatApp.models import * 469 | 470 | class ChatConsumer(AsyncWebsocketConsumer): 471 | 472 | async def connect(self): 473 | self.room_name = f"room_{self.scope['url_route']['kwargs']['room_name']}" 474 | await self.channel_layer.group_add(self.room_name, self.channel_name) 475 | await self.accept() 476 | 477 | async def disconnect(self, close_code): 478 | await self.channel_layer.group_discard(self.room_name, self.channel_name) 479 | 480 | async def receive(self, text_data): 481 | text_data_json = json.loads(text_data) 482 | message = text_data_json 483 | ``` 484 | 485 | 2. Adding a `send_message` Method: 486 | 487 | ```python 488 | import json 489 | from channels.generic.websocket import AsyncWebsocketConsumer 490 | from channels.db import database_sync_to_async 491 | from ChatApp.models import * 492 | 493 | class ChatConsumer(AsyncWebsocketConsumer): 494 | 495 | async def connect(self): 496 | self.room_name = f"room_{self.scope['url_route']['kwargs']['room_name']}" 497 | await this.channel_layer.group_add(self.room_name, self.channel_name) 498 | await this.accept() 499 | 500 | async def disconnect(self, close_code): 501 | await this.channel_layer.group_discard(self.room_name, self.channel_name) 502 | 503 | async def receive(self, text_data): 504 | text_data_json = json.loads(text_data) 505 | message = text_data_json 506 | 507 | async def send_message(self, event): 508 | data = event['message'] 509 | await self.create_message(data=data) 510 | response_data = { 511 | 'sender': data['sender'], 512 | 'message': data['message'] 513 | } 514 | await self.send(text_data=json.dumps({'message': response_data})) 515 | ``` 516 | 517 | 3. Creating the `create_message` Method to Create and Save Messages: 518 | 519 | ```python 520 | import json 521 | from channels.generic.websocket import AsyncWebsocketConsumer 522 | from channels.db import database_sync_to_async 523 | from ChatApp.models import * 524 | 525 | class ChatConsumer(AsyncWebsocketConsumer): 526 | 527 | async def connect(self): 528 | self.room_name = f"room_{self.scope['url_route']['kwargs']['room_name']}" 529 | await self.channel_layer.group_add(self.room_name, self.channel_name) 530 | await self.accept() 531 | 532 | async def disconnect(self, close_code): 533 | await self.channel_layer.group_discard(self.room_name, self.channel_name) 534 | 535 | async def receive(self, text_data): 536 | text_data_json = json.loads(text_data) 537 | message = text_data_json 538 | 539 | async def send_message(self, event): 540 | data = event['message'] 541 | await self.create_message(data=data) 542 | response_data = { 543 | 'sender': data['sender'], 544 | 'message': data['message'] 545 | } 546 | await self.send(text_data=json.dumps({'message': response_data})) 547 | 548 | @database_sync_to_async 549 | def create_message(self, data): 550 | get_room_by_name = Room.objects.get(room_name=data['room_name']) 551 | if not Message.objects.filter(message=data['message']).exists(): 552 | new_message = Message(room=get_room_by_name, sender=data['sender'], message=data['message']) 553 | new_message.save() 554 | ``` 555 | 556 | In this code section, you're defining methods in your Django Channels `ChatConsumer` to receive, send, and create messages. These methods handle WebSocket communication and message storage in your Django application. 557 | 558 | #### 19. Adding a Socket Event Listener for Server Responses: 559 | 560 | ```javascript 561 | 615 | ``` 616 | 617 | 2. Adding a Function for Automatic Scrolling to the Bottom: 618 | 619 | ```javascript 620 | 626 | ``` 627 | 628 | ### 20. Testing the Code 629 | 630 | In this section, you're creating a JavaScript event listener to handle responses from the server through the WebSocket connection. It updates the chat interface with incoming messages and automatically scrolls to the bottom to display the latest messages. --------------------------------------------------------------------------------