Introduction
In this article, we will create a simple chat application using django, vue and channels.
Note: Source code for this article can be found here. You can pull it to your machine and run docker-compose up
to start.
Our chat application looks like this:
Walk thourgh
Chat consumer
Your chat consumer located in chat/consumers.py
.
This file allow you to receive message from a chat group and send it to other people in this group.
from asgiref.sync import async_to_sync
from channels.generic.websocket import AsyncWebsocketConsumer
import json
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
self.room_group_name = "chat_%s" % self.room_name
# Join room group
await self.channel_layer.group_add(self.room_group_name, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(self.room_group_name, self.channel_name)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json["message"]
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name, {"type": "chat_message", "message": message}
)
# Receive message from room group
async def chat_message(self, event):
message = event["message"]
# Send message to WebSocket
await self.send(text_data=json.dumps({"message": message}))
And here is routing configuration for your consumer chat/routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer)
]
Make sure your chat consumer is working correctly with chat/tests/test_chat_consumer.py
import pytest
from channels.testing import WebsocketCommunicator
from chat.consumers import ChatConsumer
from channels.routing import URLRouter
from django.urls import re_path
@pytest.mark.asyncio
async def test_chat_consumer():
application = URLRouter([re_path(r"ws/chat/(?P<room_name>\w+)/$", ChatConsumer)])
communicator = WebsocketCommunicator(application, "/ws/chat/newbie/")
connected, subprotocol = await communicator.connect()
assert connected
# test sending text
await communicator.send_json_to({"message": "hello"})
response = await communicator.receive_json_from()
assert response.get("message") == "hello"
# close
await communicator.disconnect()
Chat client
assets/src/components/ChatRoom.vue
is use to send and receive message from Websocket server.
<template>
<div>
<div class="top"><span><span class="name">#{{ roomName }}</span></span></div>
<div class="chat active-chat">
<div class="conversation-start">
<span>Today, 6:48 AM</span>
</div>
<div
v-for="(message, index) in messages"
:key="index"
class="bubble text"
>
{{ message }}
</div>
</div>
<div class="write">
<a
href="javascript:;"
class="write-link attach"
><i class="fas fa-paperclip"></i></a>
<input
type="text"
@keyup.enter="sendMessage()"
v-model="message"
placeholder="Send a message..."
/>
<a
@click="sendMessage()"
href="javascript:;"
class="write-link send"
>
<i class="far fa-paper-plane"></i>
</a>
<a
href="javascript:;"
class="write-link smiley"
>
<i class="far fa-smile"></i>
</a>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
@Component
export default class ChatRoom extends Vue {
@Prop() private roomName!: string;
messages: Array<string> = [];
chatSocket: WebSocket = new WebSocket(
`ws://${window.location.host}/ws/chat/${this.roomName}/`
);
message: string = "";
mounted() {
this.chatSocket.onmessage = e => {
const data = JSON.parse(e.data);
const message = data.message;
this.messages.push(message);
};
this.chatSocket.onclose = e => {
console.error("chat socket closed unexpectedly!");
};
}
sendMessage(): void {
this.chatSocket.send(
JSON.stringify({
message: this.message
})
);
this.message = "";
}
}
</script>
<style lang="scss">
@import "@/assets/scss/app.scss";
</style>
Conclusion
This is just a simple chat application, if you love this stack then you can update the code to build more features. Thanks for reading!