Reputation: 29
I am using Django channels to create a chat app. When the chat page is refreshed, the past messages disappear. Also, when I reconnect to the channel, the old messages do not show up.
Everything else seems to work fine, messages can be sent and received by the other user, message data is being saved into the database for each specific channel. No error is thrown back.
managers.py
from django.db import models
from django.db.models import Count
class ThreadManager(models.Manager):
def get_or_create_personal_thread(self, user1, user2):
threads = self.get_queryset().filter(thread_type='personal')
threads = threads.filter(users__in=[user1, user2]).distinct()
threads = threads.annotate(u_count=Count('users')).filter(u_count=2)
if threads.exists():
return threads.first()
else:
thread = self.create(thread_type='personal')
thread.users.add(user1)
thread.users.add(user2)
return thread
def by_user(self, user):
return self.get_queryset().filter(users__in=[user])
models.py
from django.db import models
from app_users.models import User
from app_chat.managers import ThreadManager
class TrackingModel(models.Model):
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Thread(TrackingModel):
THREAD_TYPE = (
('personal', 'Personal'),
('group', 'Group')
)
name = models.CharField(max_length=50, null=True, blank=True)
thread_type = models.CharField(
max_length=15, choices=THREAD_TYPE, default='group')
users = models.ManyToManyField(User)
objects = ThreadManager()
def __str__(self):
if self.thread_type == 'personal' and self.users.count() == 2:
return f'{self.users.first()} and {self.users.last()}'
return f'{self.name}'
class Message(TrackingModel):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
sender = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField(blank=False, null=False)
def __str__(self) -> str:
return f'From <Thread - {self.thread}>'
consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from app_users.models import User
from app_chat.models import Thread, Message
from asgiref.sync import sync_to_async
from channels.db import database_sync_to_async
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
me = self.scope['user']
other_username = self.scope['url_route']['kwargs']['username']
other_user = await sync_to_async(User.objects.get)(username=other_username)
self.thread_obj = await sync_to_async(Thread.objects.get_or_create_personal_thread)(me, other_user)
self.room_name = f'personal_thread_{self.thread_obj.id}'
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()
print(f"Connected to server ==")
async def disconnect(self, close_code):
# Leave 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']
username = text_data_json['username']
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'username': username,
}
)
await self.store_message(message)
# Receive message from room group
async def chat_message(self, event):
message = event['message']
username = event['username']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message,
'username': username,
}))
# print(f'{username} : {message} Message recieved')
@database_sync_to_async
def store_message(self, message):
Message.objects.create(
thread=self.thread_obj,
sender=self.scope['user'],
text=message
)
room.html
<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br>
<input id="chat-message-input" type="text" size="100"><br>
<input id="chat-message-submit" type="button" value="Send">
{{ room_name|json_script:"room-name" }}
{{ request.user.username|json_script:"user_username"}}
<script>
const user_username = JSON.parse(document.getElementById('user_username').textContent);
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/chat/'
+ roomName
+ '/'
);
chatSocket.onopen = function(e) {
console.log('Connection is open')
};
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
document.querySelector('#chat-log').value += (data.username + ': ' + data.message + '' + '\n');
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
document.querySelector('#chat-message-submit').onclick = function(e) {
const messageInputDom = document.querySelector('#chat-message-input');
const message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message,
'username': user_username,
}));
messageInputDom.value = '';
};
</script>
</body>
</html>
Upvotes: 1
Views: 914
Reputation: 31
This is normal behavior for the WebSockets protocol, after you connect to a WebSocket only new messages will show up, if the connection closes then the message history will disappear. You will need to implement some sort of function in order to retrieve old messages and send them on connect.
Upvotes: 2