EastSw
EastSw

Reputation: 1067

Implementing simple Server Sent Event Stream with Django Channels

Django Channels docs has following basic example of a Server Sent Events. AsyncHttpConsumer

from datetime import datetime
from channels.generic.http import AsyncHttpConsumer

class ServerSentEventsConsumer(AsyncHttpConsumer):
    async def handle(self, body):
        await self.send_headers(headers=[
            (b"Cache-Control", b"no-cache"),
            (b"Content-Type", b"text/event-stream"),
            (b"Transfer-Encoding", b"chunked"),
        ])
        while True:
            payload = "data: %s\n\n" % datetime.now().isoformat()
            await self.send_body(payload.encode("utf-8"), more_body=True)
            await asyncio.sleep(1)

I want to accept messages sent via channel_layer and send them as events.

I changed the handle method, so it subscribes the new channel to a group. And I'm planning to send messages to the channel layer via channel_layer.group_send

But I couldn't figure out how to get the messages sent to the group, within handle method. I tried awaiting for the channel_layer.receive, it doesn't seem to work.

class ServerSentEventsConsumer(AsyncHttpConsumer):
    group_name = 'my_message_group'

    async def myevent(self, event):
        # according to the docs, this method will be called \
        #    when a group received a message with type 'myevent'
        # I'm not sure how to get this event within `handle` method's while loop.
        pass

    async def handle(self, body):

        await self.channel_layer.group_add(
            self.group_name,
            self.channel_name
        )
        await self.send_headers(headers=[
            (b"Cache-Control", b"no-cache"),
            (b"Content-Type", b"text/event-stream"),
            (b"Transfer-Encoding", b"chunked"),
        ])
        while True:
            payload = "data: %s\n\n" % datetime.now().isoformat()
            result = await self.channel_receive()
            payload = "data: %s\n\n" % 'received'

I'm sending the messages to channel_layer like below: ( from a management command)

def send_event(event_data):
    group_name = 'my_message_group'
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        group_name,
        {
            'type': 'myevent',
            'data': [event_data]
        }
    )

Upvotes: 4

Views: 2751

Answers (1)

OlivierM
OlivierM

Reputation: 3192

I had the same issue and I even went to dig into Django Channels code but without success.

... Until I found this answer in this (still opened) issue: https://github.com/django/channels/issues/1302#issuecomment-508896846

That should solve your issue.

In your case the code would be (or something quite similar):

class ServerSentEventsConsumer(AsyncHttpConsumer):
    group_name = 'my_message_group'

    async def http_request(self, message):
        if "body" in message:
            self.body.append(message["body"])
        if not message.get("more_body"):
            await self.handle(b"".join(self.body))

    async def myevent(self, event):
        # according to the docs, this method will be called \
        #    when a group received a message with type 'myevent'
        # I'm not sure how to get this event within `handle` method's while loop.
        pass

    async def handle(self, body):
        await self.channel_layer.group_add(
            self.group_name,
            self.channel_name
        )
        await self.send_headers(headers=[
            (b"Cache-Control", b"no-cache"),
            (b"Content-Type", b"text/event-stream"),
            (b"Transfer-Encoding", b"chunked"),
        ])

Upvotes: 3

Related Questions