Search code examples
python-3.6python-asynciodjango-channels

asyncio 'function' object has no attribute 'send'


I'm trying to send message to the client every 30 seconds till client disconnects in django channels. Below is the piece of code written to achieve it using asyncio. But getting the error "AttributeError: 'function' object has no attribute 'send'". I haven't used asyncio before, so tried many possibilities and all of them results in some kind of error (because of my inexperience). Could someone please help me how can this be solved.

Below is the code :

class HomeConsumer(WebsocketConsumer):
    def connect(self):
        self.room_name = "home"
        self.room_group_name = self.room_name
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name,
            self.channel_name
        )
        self.accept()
        self.connected = True
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        task = loop.create_task(self.send_response)
        loop.run_until_complete(task)

    async def send_response(self):
        while self.connected:
            sent_by = Message.objects.filter(notification_read=False).exclude(
                last_sent_by=self.scope["user"]).values("last_sent_by__username")

            self.send(text_data=json.dumps({
                'notification_by': list(sent_by)
            }))
            asyncio.sleep(30)

    
    def disconnect(self, close_code):

        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name,
            self.channel_name
        )
        self.connected = False

something might be wrong at below portion of the code i believe:

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
task = loop.create_task(self.send_response)
loop.run_until_complete(task)

using loop = asyncio.get_event_loop() instead of creating new_event_loop() results in :

RuntimeError: There is no current event loop in thread 'ThreadPoolExecutor-0_0'.

Solution

  • I'm posting this solution as answer because i searched a lot on how to send data to client without client requesting in django-channels. But couldn't find any complete explanation or answers. So hope this would help someone who is in the situation i was in.

    Thanks to user4815162342 for the help he provided for solving the issue i had.

    class HomeConsumer(AsyncWebsocketConsumer):
        async def connect(self):
            self.room_name = "home"
            self.room_group_name = self.room_name
            await self.channel_layer.group_add(
                self.room_group_name,
                self.channel_name
            )
            await  self.accept()
            self.connected = True
            try:
                loop = asyncio.get_event_loop()
            except:
                loop = asyncio.new_event_loop()
                asyncio.set_event_loop(loop)
            loop.create_task(self.send_response())
    
        async def send_response(self):
            while self.connected:
                sent_by = Message.objects.filter(notification_read=False).exclude(
                    last_sent_by=self.scope["user"]).values("last_sent_by__username")
    
                await self.send(text_data=json.dumps({
                    'notification_by': list(sent_by)
                }))
                await asyncio.sleep(30)
    
        async def disconnect(self, close_code):
    
            await self.channel_layer.group_discard(
                self.room_group_name,
                self.channel_name
            )
            self.connected = False
    

    If there is any issue or obsolete usage please correct me