How to properly send response to React Frontend from Django Channels?

61 Views Asked by At

I am trying to use Django Channels to implement long-polling for a React frontend web app and Django REST backend. I believe that much of what I have is working to some degree, but some thing(s) must be incorrectly configured or coded to produce unexpected results.


UPDATE: It seems that the problem lies in the axios.get(...) request. When swapping out that request with a fetch(...), that fetch receives a response every single time, whereas the Axios call gets a response every other time on average. Unsure of the solution to this at this time.


In short, the problem that I am receiving is that when the Django Channels Consumer sends back a response, it does not immediately go back to the frontend (per console.log(...)s in the code); rather, it seems the Consumer must send another response and then the previous or both responses appear in the frontend.

I am trying to implement using AsyncHttpConsumer because Websockets are not possible in our use-case.

Asgi.py

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
django_asgi_app = get_asgi_application()

application = ProtocolTypeRouter(
    {
        "http": URLRouter(
            longpoll_urlpatterns + [re_path(r"", django_asgi_app)]
        ),
    }
)

Routing.py

longpoll_urlpatterns = [
    # Tried using re_path but did not work
    path("analysisSubscription/<int:analysisId>/", consumers.AnalysisConsumer.as_asgi()),
]

Consumers.py

class AnalysisConsumer(AsyncHttpConsumer):
    async def handle(self, body):
        print("In Handle")
        print(self.scope)
        self.analysisId = self.scope["url_route"]["kwargs"]["analysisId"]
        self.analysis_group_name = f"analysis_{self.analysisId}"

        # Register with the appropriate channel
        await self.channel_layer.group_add(self.analysis_group_name, self.channel_name)
        
        await self.send_headers(headers=[
            (b"Content-type", b"application/json"),
            (b"Access-Control-Allow-Origin", b"*"),
            (b"Access-Control-Allow-Headers", b"Origin, X-Requested-With, Content-Type, Accept, Authorization"),])
                
        #The server won't send the headers unless we start sending the body
        await self.send_body(b"", more_body=True)

        print("Registered consumer for ID: ", self.analysisId, " and group: ", self.analysis_group_name)

       # await self.channel_layer.group_send(self.analysis_group_name, {"type": "analysis.update", "text": self.analysisId})

    async def http_request(self, message):
        print("In Request")
        print(message)
        if "body" in message:
            self.body.append(message["body"])
        if not message.get("more_body"):
            try:
                await self.handle(b"".join(self.body))
            except:
                print("Stopping")
                # If something goes wrong, disconnect
                # In the parent this ALWAYS disconnects and thus long-polling breaks
                await self.disconnect()
                raise StopConsumer()

    async def disconnect(self):
        print("Disconnecting!")
        await self.channel_layer.group_discard(self.analysis_group_name, self.channel_name)
    
    async def analysis_update(self, event):
        print(event)
        print("Inside Analysis Consumer")
        analysisId = event['id']
        analysisData = ""

        try:
            analysisData = await self.getAnalysis(analysisId)
            analysisData = json.dumps(analysisData)
        except Exception as ex:
            print("Failed to retrieve Analysis object: {ex}")
            return

        print("Retrieved analysis:\n\t", analysisData)
        await self.send_body(analysisData.encode('utf-8'))
        print("Sent the response")

        await asyncio.sleep(1)

        await self.http_disconnect(None)

    @database_sync_to_async
    def getAnalysis(self, id):
        return AnalysisSerializer(Analysis.objects.filter(id=id)[0]).data

Then, in the Views.py file during an update of an Analysis, I call the below line to communicate to the Consumer group and call the previously defined function, analysis_update.

async_to_sync(layers.group_send)(f"analysis_{idAnalysis}", {"type": "analysis.update", "id": idAnalysis})

The frontend ReactJS code loops by basically doing the below and checking the response.

await axios.get(ApiUrl.analysisSubscribe(analysisId), {
            timeout: 60000,
        })
0

There are 0 best solutions below