Search code examples
djangodjango-channels

Send a message from a celery background task to the browser with Django Channels


I have a long running task running on Celery. When it is done, I want to send a message to the browser that will simply instruct it to refresh the page. To do that, I want to use channels V2.

It's not clear from the docs how to achieve that.

This is what I have in my background task, but I'm not sure how to set up the Consumer

@shared_task
def my_task():
    time.sleep(5)
    Channel('my-background-task').send({"refresh": True})

Solution

  • class ReloadConsumer(WebsocketConsumer):
        def connect(self):
            self.group_name = self.scope['user']
            print(self.group_name)  # use this for debugging not sure what the scope returns
    
            # Join group
            async_to_sync(self.channel_layer.group_add)(
                self.group_name,
                self.channel_name
            )
            self.accept()
    
        def disconnect(self, close_code):
            # Leave group
            async_to_sync(self.channel_layer.group_discard)(
                self.group_name,
                self.channel_name
            )
    
        def reload_page(self, event):
            # Send message to WebSocket
            self.send(text_data=json.dumps({
                'reload': True
            }))
            self.disconnect()
    

    Then when your celery task is completed you send a reload_page message to the relevant group.

    @shared_task
    def my_task():
        ... do your task ...
        group_name = get_user_name()  # Find out way to get same as what is printed on connect()
    
        channel_layer = get_channel_layer()
        # Trigger reload message sent to group
        async_to_sync(channel_layer.group_send)(
            group_name,
            {'type': 'reload_page'}
        )
    

    Once you can successfully create a group for the user when they start the celery task, then send a message to the user's group on completion it's as simple as adding this script:

    webSocket.onmessage = function() {
        window.location.reload();
    }