Create App
1.type python manage.py startapp dialogue dialogue is an example of app name
2.go to the file dialogue/models.py and declare your class
from datetime import datetime
from django.contrib.auth.models import User
class Chat(models.Model):
title = models.CharField(max_length=200, blank=True)
users = models.ManyToManyField(User)
last_msg = models.IntegerField(blank=True,null=True,default=0)
def __str__(self):
return self.title
class Message(models.Model):
author = models.ForeignKey(User, on_delete=models.DO_NOTHING)
date_time = models.DateTimeField(default=datetime.now, blank = True)
text = models.TextField()
is_readed = models.BooleanField(default=False)
chat = models.ForeignKey(Chat, on_delete=models.CASCADE)
def __str__(self):
return self.text
class ChatNotifications(models.Model):
title = models.CharField(max_length=200, blank=True)
userid = models.ForeignKey(User, on_delete=models.CASCADE)
messageid = models.ForeignKey(Message, on_delete=models.CASCADE)
def __str__(self):
return self.title
3.Go to your app folder and open settings.py then declare your app under INSTALLED_APPS
INSTALLED_APPS = [
...
'dialogue.apps.DialogueConfig',
...
]
4.Create the migration, make sure in the terminal you are in the right folder and pipenv shell is running then type python manage.py makemigrations dialogue this will create the necessary python files
5.Make the complete migration to create the tables in the datbase by typing python manage.py migrate
Configuration Admin Area
Open dialogue/admin.py file and import the new models and register them under admin:
from .models import Chat, Message, ChatNotifications
class ChatAdmin(admin.ModelAdmin):
list_display = ('title', 'last_msg')
list_display_links = ('title', 'last_msg')
search_fields = ('title', 'last_msg')
list_per_page = 20
admin.site.register(Chat, ChatAdmin)
class MessageAdmin(admin.ModelAdmin):
list_display = ('author','date_time','chat','is_readed')
list_display_links = ('author','date_time')
search_fields = ('author','date_time','chat','is_readed')
list_per_page = 20
admin.site.register(Message,MessageAdmin)
Build Front Facing Page
Open file dialogue/views.py and Bring the models we need
from django.shortcuts import render
from django.template.loader import render_to_string
from .models import Chat, Message, ChatNotifications
from django.http import JsonResponse
2.Define index page for dialogue:
#if post request save new message and make new notifications, send back html of new message
if request.method == 'POST':
chat = Chat.objects.get(id = request.POST['chat'],users = request.user.id)
if chat:
msg = Message(author_id = request.user.id, text = request.POST['text'], chat_id = chat.id)
msg.save()
for chat_users in chat.users.all():
if chat_users.id and chat_users.id is not request.user.id:
notify = ChatNotifications( userid_id = chat_users.id, messageid_id = msg.id, title = "New message to user "+chat_users.username)
notify.save()
if msg.id:
new_msgs = Message.objects.order_by('id').filter(chat_id = chat.id, id__gt = chat.last_msg)
chat.last_msg = msg.id
chat.save()
context = {
'chat': {
'messages': new_msgs
}
}
return render(request, 'dialogue/parts/_message.html', context)
else:
return JsonResponse({'error':'Message not saved'}, safe=False)
#get all chats and messages for current user when page is loading
chats = Chat.objects.order_by('-last_msg').filter(users = request.user.id)
if chats:
if chats.count() == 1:
chats[0].messages = Message.objects.order_by('id').filter(chat = chats[0].id)
else:
for chat_obj in chats:
chat_obj.messages = Message.objects.order_by('id').filter(chat = chat_obj.id)
context = {
'chats': chats
}
return render(request, 'dialogue/index.html', context)
This function will render page on first load and add new messages on form submiting
3.Create function for ajax requests
# mark messages as readed if have get request
if request.method == "GET" and "mark_as_read" in request.GET:
messages = Message.objects.order_by('id').filter(chat = request.GET["mark_as_read"], is_readed=False)
for msg in messages:
if msg.author_id is not request.user.id:
msg.is_readed = True
msg.save()
notify = ChatNotifications.objects.filter(userid_id = request.user.id, messageid_id = msg.id)
if notify:
notify.delete()
return False
#get notification count
if request.method == "GET" and "get_notify_count" in request.GET:
notify = ChatNotifications.objects.filter(userid_id = request.user.id)
context = {
'notifications' : notify.count(),
}
return JsonResponse(context)
# if no get request just check for new messages and update chat
return_obj = {}
return_chats = {}
return_obj_tmp = {}
#chet for new notifications
notifications = ChatNotifications.objects.order_by('id').filter(userid_id = request.user.id)
if notifications:
#if hve notifications for this user get all new messages
for notify in notifications:
msg = Message.objects.get(pk = notify.messageid_id)
#save messages in dict as (chat_id = list of messages)
if msg.chat_id not in return_obj_tmp:
return_obj_tmp = {msg.chat_id:{'messages':{msg.id : msg}}}
else:
return_obj_tmp[msg.chat_id]['messages'].update({msg.id : msg})
#render html for each message
for i, chat in return_obj_tmp.items():
chat_msg = {}
for j, msgs in chat['messages'].items():
print(j)
chat_messages = render_to_string('dialogue/parts/_message.html', {'message': msgs},request)
chat_msg.update({j:chat_messages})
return_obj.update({i:chat_msg})
#get user chats
chats = Chat.objects.order_by('last_msg').filter(users = request.user.id)
#render html for each chat
if chats:
for chat in chats:
chat_html = render_to_string('dialogue/parts/_chats.html', {'chat': chat},request)
return_chats.update({chat.id:chat_html})
context = {
'notifications' : notifications.count(),
'chats' : return_chats,
'messages': return_obj
}
return JsonResponse(context)
This function will receive ajax requests and send back data to display.
Full views.py file
Creating URLs
1.Create file urls.py in app folder
2.Add URL rules to this file
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='dialogue'),
path('reload/',views.reload, name='reload')
]
3.In the main app folder, oper urls.py file and include the file you created
urlpatterns = [
...
path('dialogue/', include('dialogue.urls')),
...
]
Create HTML Template for this App
1.Create dialogue folder inside templates
2.Create index.html file inside templates/dialogue folder and copy html code in this file
{% extends 'base.html' %}
{% load static %}
{% block content %}
{% if chats %}
{% include 'dialogue/parts/_chats.html' %}
{% endif %}
{% if chats %}
{% if chats.count == 1 %}
{% if chats.0.messages %}
{% include 'dialogue/parts/_message.html' %}
{% endif %}
{% else %}
{% for chat in chats %}
{% if chat.messages %}
{% include 'dialogue/parts/_message.html' %}
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
{% endblock %}
3.Create folder “parts“ in “templates/dialogue“ folder.
Inside this new folder we need to add two new files:
_chats.html
{{chats.0.title}}
May 23, 2020 at 11:44 am
{% elif chats.count > 1 %}
{% for chat in chats %}
{{chat.title}}
May 23, 2020 at 11:44 am
{% endfor %}
{% elif chat %}
{{chat.title}}
May 23, 2020 at 11:44 am
{% endif %}
_message.html
{{ message.author.first_name }} {{ message.author.last_name }}
{{ message.date_time | date:"M d, Y at H:m "}}
{{ message.text }}
{% else %}
{{ message.author.first_name }} {{ message.author.last_name }}
{{ message.date_time | date:"M d, Y at H:m "}}
{{ message.text }}
{% endif %}
{% endfor %}
{% elif chats.0.messages %}
{% for message in chats.0.messages %}
{% if message.author.id == user.id %}
{{ message.author.first_name }} {{ message.author.last_name }}
{{ message.date_time | date:"M d, Y at H:m "}}
{{ message.text }}
{% else %}
{{ message.author.first_name }} {{ message.author.last_name }}
{{ message.date_time | date:"M d, Y at H:m "}}
{{ message.text }}
{% endif %}
{% endfor %}
{% elif message %}
{% if message.author.id == user.id %}
{{ message.author.first_name }} {{ message.author.last_name }}
{{ message.date_time | date:"M d, Y at H:m "}}
{{ message.text }}
{% else %}
{{ message.author.first_name }} {{ message.author.last_name }}
{{ message.date_time | date:"M d, Y at H:m "}}
{{ message.text }}
{% endif %}
{% endif %}
Last thing, we need to add new js file. Go to our static folder and create file “dialogue.js“ with this js code
function ajax_chat(form, callbacks = 'default_chat_callback') {
var reg_functions = [];
var type = form.getAttribute('method');
var url = form.getAttribute('action');
var formData = new FormData(form);
reg_functions.push(callbacks);
if (reg_functions.length > 0) {
//only fetch data if a function is setup
var r = new XMLHttpRequest();
r.addEventListener('readystatechange', function(event){
if (this.readyState === 4){
if (this.status === 200){
var data = { "data": parseJsonChat(r.responseText)};
reg_functions.forEach(function (func) {
data.form = form;
return window[func](data);
});
}
}
})
r.open(type, url , true);
r.send(formData);
}
return false;
}
function default_chat_callback(data){
var form = data.form;
var msgDiv = form.getElementsByClassName('form-msg');
if(!data.data.msg){
data.data.msg='Form submited';
}
if(!data.data.class){
data.data.class = 'bg-success';
}
msgDiv[0].innerHTML=data.data.msg;
msgDiv[0].classList.add(data.data.class);
msgDiv[0].style.display = "block";
form.reset();
}
//send new message
function send_chat_message(data){
var msg = data.data;
var form = data.form;
var dialogId = form.querySelector('[name="chat"]').value;
var chat = document.getElementById('dialouge'+dialogId);
chat.insertAdjacentHTML("beforeend", msg);
chat.parentNode.scrollTop = chat.scrollHeight;
}
//check new chats and messages. If have new load them
function chatReloading(){
var r = new XMLHttpRequest();
var url = window.location.href + 'reload/';
r.addEventListener('readystatechange', function(event){
if (this.readyState === 4){
if (this.status === 200){
var data = parseJson(r.responseText);
resetChatList(data.chats);
updateChat(data);
setChatNotificationsCount(data.notifications);
setTimeout(chatReloading, 10000);
}
}
})
r.open('get', url , true);
r.send();
}
//display new chats in chat list
function resetChatList(chatList){
var chatContainer = document.getElementById("dialog_container");
for(var chatId in chatList){
var chatLabel = document.getElementById("dialougeOpener"+chatId);
if(!!chatLabel){
var isActive = chatLabel.classList.contains("active");
var div = document.createElement('div');
div.innerHTML = chatList[chatId];
if(!!isActive){
chatLabel.replaceWith(div.children[0]);
openDialouge(chatId);
}else{
chatLabel.remove();
var activeChat = chatContainer.getElementsByClassName('active');
activeChat[0].insertAdjacentHTML("afterend", chatList[chatId]);
}
}else{
var activeChat = chatContainer.getElementsByClassName('active');
activeChat[0].insertAdjacentHTML("afterend", chatList[chatId]);
var messagesContainer = document.getElementById('messages_container');
var newDialogue = document.createElement('div');
newDialogue.setAttribute('id',chatId)
messagesContainer.insertAdjacentHTML("beforeend", "
");
}
}
}
//mark messages as readed
function MarkChatAsRead(id){
var r = new XMLHttpRequest();
var url = window.location.href + 'reload/?mark_as_read='+id;
r.open('get', url , true);
r.send();
}
//if have new messages display them
function updateChat(data){
if(!!data.messages){
var messages = data.messages;
for (var chatId in messages) {
var msgObject = data.messages[chatId];
if(!!msgObject){
var chat = document.getElementById('dialouge'+chatId);
for(var id in msgObject ){
var checkMsg = document.getElementById(id);
if(!checkMsg){
chat.insertAdjacentHTML("beforeend", msgObject[id]);
chat.parentNode.scrollTop = chat.scrollHeight;
}
}
}
}
}
}
//check if have message notification
function chatNotificationsReloading(){
var r = new XMLHttpRequest();
var url = '/dialogue/reload/?get_notify_count=true';
r.addEventListener('readystatechange', function(event){
if (this.readyState === 4){
if (this.status === 200){
var data = parseJson(r.responseText);
setChatNotificationsCount(data.notifications);
setTimeout(chatNotificationsReloading, 10000);
}
}
})
r.open('get', url , true);
r.send();
}
//display new chat notifications
function setChatNotificationsCount(msgCount = 0){
if(msgCount > 0){
document.getElementById('messages-count').style.display = 'block';
document.getElementById('messages-count').innerHTML = msgCount;
}else{
document.getElementById('messages-count').style.display = 'none';
}
}
//check if opened dialogue page or other one and start circled function we need
var chats = document.getElementById('dialog_container');
if(!!chats){
setTimeout(chatReloading, 3000);
}else{
setTimeout(chatNotificationsReloading, 3000);
}
Do not forget to run “python manage.py collectstatic“ command in our terminal, and add this file in “base.html“
NOTICE: in _header.html file we need to change one element.
To this span near message link
need to add id="messages-count" and style="display:none"
Full list of file for this app