New message notification django-channels
I know someone has already asked this question in stackoverflow, in fact this is the question: New chat message notification Django Channels, the answer they gave is exactly what I want to do, also my code or project has the same code of the person who ask. The problem is the following, I took a deep look and tried it in many ways, unfortunately, it did not work for me and I did not understand it very well. I would love someone to explain it to me, and teach me either with my modified code or what should I do to adapt the notification system when the user receives a new message, my code is the following:
consumers.py
import asyncio import json from django.contrib.auth import get_user_model from channels.consumer import AsyncConsumer from channels.db import database_sync_to_async from .models import Thread, ChatMessage class ChatConsumer(AsyncConsumer): async def websocket_connect(self, event): print("connected", event) other_user = self.scope['url_route']['kwargs']['username'] me = self.scope['user'] # print(other_user, me) thread_obj = await self.get_thread(me, other_user) self.thread_obj = thread_obj chat_room = f"thread_{thread_obj.id}" self.chat_room = chat_room await self.channel_layer.group_add( chat_room, self.channel_name ) await self.send({ "type": "websocket.accept" }) # await asyncio.sleep(10) async def websocket_receive(self, event): # when a message is received from the websocket print("receive", event) front_text = event.get('text', None) if front_text is not None: loaded_dict_data = json.loads(front_text) msg = loaded_dict_data.get('message') user = self.scope['user'] username = 'default' if user.is_authenticated: username = user.username myResponse = { 'message': msg, 'username': username, } await self.create_chat_message(user, msg) # broadcast the message event to be send await self.channel_layer.group_send( self.chat_room, { "type": "chat_message", "text": json.dumps(myResponse) } ) async def chat_message(self, event): # sends the actual message await self.send({ "type": "websocket.send", "text": event['text'] }) async def websocket_disconnect(self, event): # when the socket connects print("disconnected", event) @database_sync_to_async def get_thread(self, user, other_username): return Thread.objects.get_or_new(user, other_username)[0] @database_sync_to_async def create_chat_message(self, me, msg): thread_obj = self.thread_obj return ChatMessage.objects.create(thread=thread_obj, user=me, message=msg)
models.py
from django.db import models from django.conf import settings from django.db import models from django.db.models import Q class ThreadManager(models.Manager): def by_user(self, user): qlookup = Q(first=user) | Q(second=user) qlookup2 = Q(first=user) & Q(second=user) qs = self.get_queryset().filter(qlookup).exclude(qlookup2).distinct() return qs def get_or_new(self, user, other_username): # get_or_create username = user.username if username == other_username: return None qlookup1 = Q(first__username=username) & Q(second__username=other_username) qlookup2 = Q(first__username=other_username) & Q(second__username=username) qs = self.get_queryset().filter(qlookup1 | qlookup2).distinct() if qs.count() == 1: return qs.first(), False elif qs.count() > 1: return qs.order_by('timestamp').first(), False else: Klass = user.__class__ user2 = Klass.objects.get(username=other_username) if user != user2: obj = self.model( first=user, second=user2 ) obj.save() return obj, True return None, False class Thread(models.Model): first = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_first') second = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_second') updated = models.DateTimeField(auto_now=True) timestamp = models.DateTimeField(auto_now_add=True) objects = ThreadManager() @property def room_group_name(self): return f'chat_{self.id}' def broadcast(self, msg=None): if msg is not None: broadcast_msg_to_chat(msg, group_name=self.room_group_name, user='admin') return True return False class ChatMessage(models.Model): thread = models.ForeignKey(Thread, null=True, blank=True, on_delete=models.SET_NULL) user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='sender', on_delete=models.CASCADE) message = models.TextField() timestamp = models.DateTimeField(auto_now_add=True)
thread.html
{% extends "base.html" %} {% block content %} <h3>Thread for {% if user != object.first %}{{ object.first }}{% else %}{{ object.second }}{% endif %}</h3> <ul id='chat-items'> {% for chat in object.chatmessage_set.all %} <li>{{ chat.message }} via {{ chat.user }}</li> {% endfor %} </ul> <form id='form' method='POST'> {% csrf_token %} <input type='hidden' id='myUsername' value='{{ user.username }}' /> {{form.as_p }} <input type='submit' class='btn btn-primary'/> </form> {% endblock %} {% block script %} <script src='https://cdnjs.cloudflare.com/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.js'> </script> <script> // websocket scripts // console.log(window.location) var loc = window.location var formData = $("#form") var msgInput = $("#id_message") var chatHolder = $("#chat-items") var me = $("#myUsername").val() var wsStart = 'ws://' if (loc.protocol == 'https:') { wsStart = 'wss://' } var endpoint = wsStart + loc.host + loc.pathname + '/' var socket = new ReconnectingWebSocket(endpoint) socket.onmessage = function(e){ console.log("message", e) var chatDataMsg = JSON.parse(e.data) chatHolder.append("<li>" + chatDataMsg.message + " via " + chatDataMsg.username + "</li>") } socket.onopen = function(e){ console.log("open", e) formData.submit(function(event){ event.preventDefault() var msgText = msgInput.val() // chatHolder.append("<li>" + msgText + " via " + me + "</li>") var finalData = { 'message': msgText } socket.send(JSON.stringify(finalData)) formData[0].reset() }) } socket.onerror = function(e){ console.log("error", e) } socket.onclose = function(e){ console.log("close", e) } </script> {% endblock %}
user_home.html
{% load static %} <!DOCTYPE html> <html> <head> <title>HOME</title> <link rel='stylesheet' type='text/css' href="{% static 'css/styles.css' %}"> <link rel='stylesheet' type='text/css' href="{% static 'css/navbar.css' %}"> <link rel='stylesheet' type='text/css' href="{% static 'css/parallax.css' %}"> <link rel='stylesheet' type='text/css' href="{% static 'css/footer.css' %}"> <link rel='stylesheet' type='text/css' href="{% static 'css/loginbuttons.css' %}"> <link rel='stylesheet' type='text/css' href="{% static 'css/login2.css' %}"> </head> <body> <style> a{text-decoration:none} body { font-family: Arial, Helvetica, sans-serif; } .notification { background-color: #f6f5f7; color: white; text-decoration: none; padding: 4px 5px; position: fixed; top: 70px; right: 5px; display: inline-block; border-radius: 50%; } .notification:hover { background: rgb(175, 175, 175); } .notification .badge { position: fixed; top: 64px; right: 4px; padding: 0px 5px; border-radius: 50%; background-color: red; color: white; } </style> <!-- The pallarax effect of the portada --> <div id="parallax"> <h1>MI FAMILIA</h1> <h2>ES UN</h2> <h3>DESASTRE</h3> </div> <!--Login button--> <a class="button type1" href="/logout"> Logout </a> <label style="position: fixed; top: 19px; right: 170px; color: #fff;"> {% csrf_token %} {{ user.username }} </label> <!--The chat button and help--> <a href="/messages" class="notification"> <img src="https://img.pngio.com/text-message-icon-png-126137-free-icons-library-text-message-icon-png-512_512.jpg" style="width: 30px; height: 30px; position: relative; top: 4px;"> <span class="badge">1</span> <!--This is the span element--> </a> <!--the presentation--> <div class="parallax"></div> <div style="height:500px;background-color:#737585;font-size:36px; color: rgb(223, 219, 219);"> Scroll Up and Down this page to see the parallax scrolling effect. This div is just here to enable scrolling. Tip: Try to remove the background-attachment property to remove the scrolling effect. </div> <div class="parallax"></div> <!--Footer--> <div id="site-content"> <h6 style="color: aliceblue; position: absolute; right: 300px; top: -80px; font-size: 70px;">Qué estas Esperando?</h6> </div> <a href="#"> <button>TERAPIA</button> </a> <!-- the navbar define --> <header> <input type='checkbox' id='toggle' style='display:none;' /> <label class='toggle-btn toggle-btn__cross' for='toggle'> <div class="bar"></div> <div class="bar"></div> <div class="bar"></div> </label> <nav> <ul> <li><a href="/home">Inicio</a></li> <li><a href="#">Ayuda</a></li> <li><a href="#">Consultores</a></li> <li><a href="#">Contactanos</a></li> </ul> </nav> </header> <script src="{% static 'js/styles.js' %}"></script> <script src="{% static 'js/footer.js' %}"></script>
user_home.html is the home page for the user, thread is the one for the chat room, in user_home.html I comment the span element I want to show when there is a new unread message, the class of the span is "badge", and when they click on the message button that contains the badge the badge desappear. thanks for your help.