Search code examples
pythondjangoasynchronousdjango-channelsdjango-signals

Send data to websocket via signals


I hope you all are doing well. I have been trying to create a notification system that will push messages to websocket via post_save signals from database. I have a Notification model that creates notification and I want to push notifications whenever a notification is created to the appropriate user, any help in it would be much appreciated.

Signals.py

def send_message(event):
    '''
    Call back function to send message to the browser
    '''
    message = event['text']
    channel_layer = channels.layers.get_channel_layer()
    # Send message to WebSocket
    print("Sending message to websocket")
    async_to_sync(channel_layer.send)(text_data=json.dumps(
        message
    ))

@receiver(post_save,sender=Notification)
def notification_handler(sender,instance,created,*args,**kwargs):
    message={
        'text':instance.text
    }
    print(message)
    user=str(instance.to.pk)
    groupname=f"user_{user}"
    channel_layer = channels.layers.get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        groupname,
        {
            'type': 'send_message',
            'text': message
        }
    )

This is how my consumer.py looks like

consumer.py

class NotificationConsumer(AsyncWebsocketConsumer):
    async def websocket_connect(self,event):
        print("connected",event)
        self.channel_layer=channels.layers.get_channel_layer()
        self.user = self.scope['url_route']['kwargs']['user_id']
        self.group_name =f"user_{self.user}"
        await self.channel_layer.group_add(
            self.group_name,
            self.channel_name
        )

        await self.accept()
    
    async def websocket_disconnect(self,event):
        print(event,"closed")    



Solution

  • The issue was resolved by creating the notifications system with the following code with consumers.py and signals.py on post_save of concerned models.

    consumers.py

    class NotificationConsumer(AsyncWebsocketConsumer):
        
        async def websocket_connect(self,event):
            print("connected",event)
            self.channel_layer=channels.layers.get_channel_layer()
            self.user = self.scope['url_route']['kwargs']['user_id']
            
            self.group_name =f"user_{self.user}"
            print(self.group_name)        
    
            await self.channel_layer.group_add(
                self.group_name,
                self.channel_name
            )
    
            await self.accept()
            user=self.scope['user']
            
            unread_notifications= await self.get_unread_notifications(user)
            unread_notifications['notification_type']="group"
    
            await self.channel_layer.group_send(
                        self.group_name,
                        {
                            'type': 'send_notification',
                            'text': unread_notifications
                        }
                    )
    
        #To fetch unread notifications which have been recorded while the user was offline
        @staticmethod
        @receiver(post_save,sender=Notification)
        def notification_handler(sender,instance,created,*args,**kwargs):
            if instance.read !=1:
                message={
                    'notification_type':"single",
                    'text':instance.text,
                    'id':instance.pk_object,
                    "type":instance.Type,
                    "timestamp":str(instance.timestamp),
                    "by":ProfileSerializer(instance.by).data
                }
                print("Inside the notification trigger",message)
                user=str(instance.to.user.pk)
                groupname=f"user_{user}"
                channel_layer = channels.layers.get_channel_layer()
                async_to_sync(channel_layer.group_send)(
                            groupname,
                            {
                                'type': 'send_notification',
                                'text': message
                            }
                        )
            
        async def send_notification(self, event):
            print("inside send notification")
            await self.send(text_data=json.dumps({
                'type': 'websocket.send',
                'text': event['text']
            }))
        @database_sync_to_async
        def get_unread_notifications(self, user):
            return Notification.objects.get_unread(user)
    
    

    models.py

    @receiver(post_save,sender=Connect)
    
    def connect_handler(sender,instance,created,*args,**kwargs):
        from django.apps import apps
        Notification= apps.get_model("notifications","Notification")
        if created and instance.confirmed == 0:     
            print("inside trigger")
            notification=Notification.objects.create(
                to=instance.user2,
                Type="connection",
                text=f"{instance.user1} sent you a connection request",
                by=instance.user1,
                pk_object=instance.user1.pk
            )
    @receiver(post_save,sender=Like)
    
    def like_handler(sender,instance,created,*args,**kwargs):
        from django.apps import apps
        Notification= apps.get_model("notifications","Notification")
        if created:
            if instance.idea != None:
                changed=instance.idea
                changed_type="Idea"
                object_id=instance.idea.pk
                element=instance.idea
            else:
                changed=instance.post
                changed_type="Post"
                object_id=instance.post.pk
                element =instance.post
            if instance.Liked_by != element.posted_by:
                notification=Notification.objects.create(
                    to=changed.posted_by,
                    Type=changed_type,
                    text=f"{instance.Liked_by.user.username} Liked your {changed_type}",
                    by=instance.Liked_by,
                    pk_object=object_id
                )