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 :)
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”.
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.
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.
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.
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...
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",
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With