This tutorial will guide you through creating a chat website using Django and Django Channels.
-
Create and enter the desired directory for project setup.
-
Create a virtual environment using pipenv or other means:
pipenv shell
-
Install Django:
pip install django
-
Create a Django project called ChatPrj:
django-admin startproject ChatPrj
-
Create an app called ChatApp:
python manage.py startapp ChatApp
-
Open the project in your code editor.
-
Create a templates folder and register it in the project's settings.
-
Register the app in the project's settings.
-
Create URLs for the app and register them in the project's URLs.
-
Install Django Channels:
pip install django-channels
-
Install Daphne:
pip install daphne
-
Add ChatApp, daphne and channels to
installed_appsinsettings.pyfile:INSTALLED_APPS = [ 'daphne', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'ChatApp', 'channels', ]
-
Create
routing.py. -
Create
consumers.py.
-
Create a model called
Room:class Room(models.Model): room_name = models.CharField(max_length=255) def __str(self): return self.room_name
-
Create another model called
Message:class Message(models.Model): room = models.ForeignKey(Room, on_delete=models.CASCADE) sender = models.CharField(max_length=255) message = models.TextField() def __str(self): return str(self.room)
-
Make migrations and migrate:
python manage.py makemigrations python manage.py migrate
-
Register the models in the
admin.pyfile:from .models import * admin.site.register(Room) admin.site.register(Message)
- Download the following HTML templates from GitHub:
index.htmlmessage.html
-
CreateRoom view:
def CreateRoom(request): return render(request, 'index.html')
-
Message view:
def MessageView(request, room_name, username): return render(request, 'message.html')
from . import views
from django.urls import path
urlpatterns = [
path('', views.CreateRoom, name='create-room'),
path('<str:room_name>/<str:username>/', views.MessageView, name='room'),
]In your index.html file, make sure to include a CSRF token in form:
{% csrf_token %}In your Django CreateRoom view, check for incoming POST requests:
if request.method == 'POST':Retrieve user-entered data:
if request.method == 'POST':
username = request.POST['username']
room = request.POST['room']Create try and except blocks to either get the room object or create it if it does not exist:
try:
get_room = Room.objects.get(room_name=room)
except Room.DoesNotExist:
new_room = Room(room_name=room)
new_room.save()Test the code to see if it works.
Next, redirect users to MessageView:
try:
get_room = Room.objects.get(room_name=room)
return redirect('room', room_name=room, username=username)
except Room.DoesNotExist:
new_room = Room(room_name=room)
new_room.save()
return redirect('room', room_name=room, username=username)- Getting the room object and returning it as well as room name and username in the context
def MessageView(request, room_name, username):
get_room = Room.objects.get(room_name=room_name)
get_messages = Message.objects.filter(room=get_room)
context = {
"messages": get_messages,
"user": username,
"room_name": room_name,
}
return render(request, 'message.html', context)- Display Messages from the Query Set in
message.html:
<div class="message" id="chatContainer">
<!-- Received messages are displayed here -->
{% for i in messages %}
{% if i.sender != user %}
<div class="receive">
<p style="color: #000;"> {{i.message}}<strong>-{{i.sender}}</strong></p>
</div>
{% else %}
<div class="send">
<p style="color: #000;">{{i.message}}</p>
</div>
{% endif %}
{% endfor %}
<!-- End of received messages -->
</div>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.
Head over to your consumers.py file
- Importing Modules:
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from ChatApp.models import *- Creating
ChatConsumer:
class ChatConsumer(AsyncWebsocketConsumer):- Create
connectMethod:
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = f"room_{self.scope['url_route']['kwargs']['room_name']}"
await self.channel_layer.group_add(self.room_name, self.channel_name)
await self.accept()- Create
disconnectMethod:
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = f"room_{self.scope['url_route']['kwargs']['room_name']}"
await self.channel_layer.group_add(self.room_name, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.room_name, self.channel_name)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.
Head to your routing.py File and Add the Following:
from django.urls import path
from .consumers import ChatConsumer
websocket_urlpatterns = [
path('ws/notification/<str:room_name>/', ChatConsumer.as_asgi()),
]Head to your asgi.py file in project folder
- Importing Modules:
import os
from django.core.asgi import get_asgi_application
# imports
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from ChatApp import routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'movie.settings')
application = get_asgi_application()- Rename
applicationtodjango_asgi_app:
django_asgi_app = get_asgi_application()- Add the Following:
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": URLRouter(
routing.websocket_urlpatterns
)
})- The Final Code:
import os
from django.core.asgi import get_asgi_application
# imports
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from ChatApp import routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'movie.settings')
django_asgi_app = get_asgi_application()
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": URLRouter(
routing.websocket_urlpatterns
)
})Head to settings.py file
- Update
ASGI_APPLICATIONin your Settings:
ASGI_APPLICATION = "ChatProject.asgi.application"- Add
channel_layersConfiguration:
CHANNEL_LAYERS = {
"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"},
}Here's the provided content in markdown format for your readme.md file:
- Head to
message.htmlFile and Create Script Tags:
<script>
</script>This step involves adding script tags to your message.html file to embed JavaScript code for handling WebSocket connections.
- Create a New WebSocket:
<script>
const websocketProtocol = window.location.protocol === "https:" ? "wss" : "ws";
const wsEndpoint = `${websocketProtocol}://${window.location.host}/ws/notification/{{room_name}}/`;
const socket = new WebSocket(wsEndpoint);
</script>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.
Handling WebSocket Connection Events:
<script>
// Determine the WebSocket protocol based on the application's URL
const websocketProtocol = window.location.protocol === "https:" ? "wss" : "ws";
const wsEndpoint = `${websocketProtocol}://${window.location.host}/ws/notification/{{room_name}}/`;
// Create a new WebSocket connection
const socket = new WebSocket(wsEndpoint);
// Successful connection event
socket.onopen = (event) => {
console.log("WebSocket connection opened!");
};
// Socket disconnect event
socket.onclose = (event) => {
console.log("WebSocket connection closed!");
};
</script><script>
// Determine the WebSocket protocol based on the application's URL
const websocketProtocol = window.location.protocol === "https:" ? "wss" : "ws";
const wsEndpoint = `${websocketProtocol}://${window.location.host}/ws/notification/{{room_name}}/`;
// Create a new WebSocket connection
const socket = new WebSocket(wsEndpoint);
// Successful connection event
socket.onopen = (event) => {
console.log("WebSocket connection opened!");
};
// Socket disconnect event
socket.onclose = (event) => {
console.log("WebSocket connection closed!");
};
// Form submit listener
document.getElementById('message-form').addEventListener('submit', function(event){
event.preventDefault();
const message = document.getElementById('msg').value;
socket.send(
JSON.stringify({
'message': message,
'room_name': '{{room_name}}',
'sender': '{{user}}',
})
);
});
</script>- Creating a
ChatConsumerwithreceiveMethod:
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from ChatApp.models import *
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = f"room_{self.scope['url_route']['kwargs']['room_name']}"
await self.channel_layer.group_add(self.room_name, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.room_name, self.channel_name)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json- Adding a
send_messageMethod:
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from ChatApp.models import *
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = f"room_{self.scope['url_route']['kwargs']['room_name']}"
await this.channel_layer.group_add(self.room_name, self.channel_name)
await this.accept()
async def disconnect(self, close_code):
await this.channel_layer.group_discard(self.room_name, self.channel_name)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json
async def send_message(self, event):
data = event['message']
await self.create_message(data=data)
response_data = {
'sender': data['sender'],
'message': data['message']
}
await self.send(text_data=json.dumps({'message': response_data}))- Creating the
create_messageMethod to Create and Save Messages:
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from ChatApp.models import *
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = f"room_{self.scope['url_route']['kwargs']['room_name']}"
await self.channel_layer.group_add(self.room_name, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.room_name, self.channel_name)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json
async def send_message(self, event):
data = event['message']
await self.create_message(data=data)
response_data = {
'sender': data['sender'],
'message': data['message']
}
await self.send(text_data=json.dumps({'message': response_data}))
@database_sync_to_async
def create_message(self, data):
get_room_by_name = Room.objects.get(room_name=data['room_name'])
if not Message.objects.filter(message=data['message']).exists():
new_message = Message(room=get_room_by_name, sender=data['sender'], message=data['message'])
new_message.save()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.
<script>
// Determine the WebSocket protocol based on the application's URL
const websocketProtocol = window.location.protocol === "https:" ? "wss" : "ws";
const wsEndpoint = `${websocketProtocol}://${window.location.host}/ws/notification/{{room_name}}/`;
// Create a new WebSocket connection
const socket = new WebSocket(wsEndpoint);
// Successful connection event
socket.onopen = (event) => {
console.log("WebSocket connection opened!");
};
// Socket disconnect event
socket.onclose = (event) => {
console.log("WebSocket connection closed!");
};
// Form submit listener
document.getElementById('message-form').addEventListener('submit', function(event){
event.preventDefault();
const message = document.getElementById('msg').value;
socket.send(
JSON.stringify({
'message': message,
'room_name': '{{room_name}}',
'sender': '{{user}}',
})
);
});
// Response from consumer on the server
socket.addEventListener("message", (event) => {
const messageData = JSON.parse(event.data)['message'];
console.log(messageData);
var sender = messageData['sender'];
var message = messageData['message'];
// Empty the message input field after the message has been sent
if (sender == '{{user}}'){
document.getElementById('msg').value = '';
}
// Append the message to the chatbox
var messageDiv = document.querySelector('.message');
if (sender != '{{user}}') { // Assuming you have a variable `currentUser` to hold the current user's name
messageDiv.innerHTML += '<div class="receive"><p style="color: #000;">' + message + '<strong>-' + sender + '</strong></p></div>';
} else {
messageDiv.innerHTML += '<div class="send"><p style="color: #000;">' + message + '</p></div>';
}
scrollToBottom();
});
</script>- Adding a Function for Automatic Scrolling to the Bottom:
<script>
function scrollToBottom() {
var chatContainer = document.getElementById("chatContainer");
chatContainer.scrollTop = chatContainer.scrollHeight;
}
</script>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.