Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Channel instead of Group when using django channels?

I 'm trying to make use of the channels project (http://channels.readthedocs.org/en/latest/index.html) on django.

While on the docs there is a good tutorial for building a Group-based websocket application(chat), I couldn't find something related to a simple push mechanism that will be client specific (so no need to use Group)

Let's say I want to build a feed aggregator with various news providers and when a user visits the homepage and waits for all the feeds to get parsed, I want to send him informational messages about which one is being parsed by the server, while he waits.

What I got now is: consumers.py

from channels import Group, Channel
from .views import sort_articles_by_date
from .soup import ProviderParser
from .models import Provider


# Connected to websocket.connect and websocket.keepalive
def ws_add(message):
    Group("news_providers_loading").add(message.reply_channel)

def ws_message(message):
    providers = Provider.objects.all()

    articles = []
    for provider in providers:
        Group("news_providers_loading").send({'content': str(provider)})
        parser = ProviderParser(provider)
        articles.extend(parser.parse_articles())

     sort_articles_by_date(articles)


 # Connected to websocket.disconnect
 def ws_disconnect(message):
     Group("news_providers_loading").discard(message.reply_channel)

routing.py

channel_routing = {
    "websocket.connect": "news_providers.consumers.ws_add",
    "websocket.keepalive": "news_providers.consumers.ws_add",
    "websocket.receive": "news_providers.consumers.ws_message",
    "websocket.disconnect": "news_providers.consumers.ws_disconnect",
}

Though it works ok, I can't help it but feel that's a bit overkill(?) Is there a way to just make use of the Channel constructor, instead of Group?

Thanks :)

like image 396
mitsest Avatar asked Jan 30 '16 16:01

mitsest


People also ask

What is a channel in Django channels?

Channels preserve the synchronous behavior of Django and add a layer of asynchronous protocols allowing users to write the views that are entirely synchronous, asynchronous, or a mixture of both. Channels basically allow the application to support “long-running connections”.

How does Django channel work?

With WebSockets (via Django Channels) managing the communication between the client and the server, whenever a user is authenticated, an event will be broadcasted to every other connected user. Each user's screen will change automatically, without them having to reload their browsers.

Can you use Django channels without Redis?

channels_redis is the only official Django-maintained channel layer supported for production use. The layer uses Redis as its backing store, and it supports both a single-server and sharded configurations as well as group support. To use this layer you'll need to install the channels_redis package.

Why Django channels are used?

Django Channels facilitates support of WebSockets in Django in a manner similar to traditional HTTP views. It wraps Django's native asynchronous view support, allowing Django projects to handle not only HTTP, but also protocols that require long-running connections, such as WebSockets, MQTT, chatbots, etc.


1 Answers

Update:

channels version = 0.9

channels are 0.9 now so some changes are required for the client to receive the message from the server:

class Content:
    def __init__(self, reply_channel):
        self.reply_channel = reply_channel

    def send(self, json):
        self.reply_channel.send({
            'reply_channel': str(self.reply_channel),
            'text': dumps(json)
        })


def ws_message(message):
    content = Content(message.reply_channel)
    content.send({'hello': 'world'})

routing.py stays the same...


channels version < 0.9

Bah, it was a bit tricky but found it.

You have to use the message's reply_channel property. So this:

Group("news_providers_loading").send({'content': str(provider)})

turns into this:

Channel(message.reply_channel).send({'content': str(provider)})

What I got now is:

from channels import Channel
from .soup import ProviderParser, sort_articles_by_date
from .models import Provider
from django.template.loader import render_to_string
from json import dumps


class Content:
    def __init__(self, reply_channel):
        self.reply_channel = reply_channel

    def send(self, json):
        Channel(self.reply_channel).send({'content': dumps(json)})


def ws_message(message):
    providers = Provider.objects.all()
    content = Content(message.reply_channel)

    content.send({'providers_length': len(providers)})

    articles = []
    for provider in providers:

        content.send({'provider': str(provider)})

        parser = ProviderParser(provider)
        articles.extend(parser.parse_articles())

    sort_articles_by_date(articles)
    html = render_to_string('news_providers/article.html', {'articles': articles})

    content.send({'html': html})

routing.py

channel_routing = {
     "websocket.receive": "news_providers.consumers.ws_message",
}

Seems lighter, though you might want to keep connect, keepalive and disconnect methods (as simple foo methods) -not entirely sure about that-!

# connect, keepalive and disconnect
def ws_foo(message):
    pass

routing.py

channel_routing = {
    "websocket.connect": "news_providers.consumers.ws_foo",
    "websocket.keepalive": "news_providers.consumers.ws_foo",
    "websocket.receive": "news_providers.consumers.ws_message",
    "websocket.disconnect": "news_providers.consumers.ws_foo",
}
like image 75
mitsest Avatar answered Oct 02 '22 16:10

mitsest