Search code examples
djangodjango-modelswebsocketdjango-rest-frameworkdjango-channels

How to use Django channels to push newly created data to client side without reloading and not completely changing the existing normal view code


Hey guys I am quite new to django and django channels. I have a small doubt related to channels. For example, if I have a post model and I created a post, for the user to view the post, they need to either reload the page or we need to redirect them to the list page to view all the posts.

What if I want to push the newly created post to the front end or to the client side without having them to reload the page? Can we use channels for that? And if we use channels for that purpose, do we need to rewrite all the codes we wrote in normal views or adding a snippet code like sending a signal when created and running an async function will do the trick? Is it difficult to implement?

Thanks


Solution

  • What if I want to push the newly created post to the front end or to the client side without having them to reload the page?
    YES, but with a caveat, that being you would have to cache all the posts written before that if the user(on the client) is on the <post_list> page. This won't be an issue for a small project but if their are too many posts it would take too much time to load.

    Can we use channels for that? YES

    And if we use channels for that purpose, do we need to rewrite all the codes we wrote in normal views or adding a snippet code like sending a signal when created and running an async function will do the trick?
    YES and no, since you have used [django-rest-framework(DRF)], the rest-framework will only work on a HTTP protocol; when you use websockets you are using WS protocol thereby to handle the events django-channels has consumers just like views in django & DRF. But you can(you should for maintaining the robustness of the code) use serializers that you wrote for [django-rest-framework].

    To demonstrate that, a scenario in which a user wrote a post and you received it on your django-channel AsyncConsumer you can use something like this:-

    from channels.db import database_sync_to_async
    @database_sync_to_async
        async def save_post(self, data):
            serializer = PostSerializer(data=data)
            serializer.is_valid(raise_exception=True)
            x = serializer.create(serializer.validated_data)#this will create the post
            return PostSerializer(x).data #this will return the serialized post data
    

    Since django-channels AsyncConsumer has all the events written in an async function and saving a new post is a synchronous function we need to use a "@database_sync_to_async"; (make sure to use await-keyword when calling the save_post function).

    To answer the later half of the question can you use django-signals? Yes, modify the above code to call that django-signal instead of using the serializer in the "save_post" function above you can serialize the data inside the django-signals. But I believe the above method would do the trick.

    From my understanding I believe that you want to notify the user regarding a new post for that every user should be connected to the same channel-group and once the save_post function is completed dispatch an event in that group with the notification.

    #inside the signals.py file
    from .models.py import <Post-model>
    from django.db.models.signals import post_save
    from django.dispatch import receiver
    from channels.layers import get_channel_layer
    from asgiref.sync import async_to_sync #since everything in django
                                           #is running synchronously.
    
    """
    when the post gets created; the below receiver function would run.
    """
    @receiver(post_save,sender=<Post-model>)
    def notify_new_post(sender,instance,created,**kwargs)
        if created:
            channel_layer = get_channel_layer()
            async_to_sync(channel_layer.group_send)(
                group=<group_name>,#the group/room that will receive the broadcast
                message= {
                    'type':"<name of the event>",
                    'payload':{<whatever notification you want to send>}
                    #before writing that payload make sure what type of 
                    #channels you are using if you are using a 
                    #"AsyncWebsocketConsumer" the payload should be in the 
                    #appropriate type thereby use json.dumps to convert it to JSON.
                    #if you are using "AsyncJsonWebsocketConsumer" then you don't 
                    #have to do it [this is just for precautionary].
                }
    )
    
    

    Hope this helps, if not keep asking on this thread.