Real-time web with Django channels

PythonPH Meetup

June 23, 2016

Mark Steve Samson

real-time web

The real-time web is a network web using technologies and practices that enable users to receive information as soon as it is published by its authors, rather than requiring that they or their software check a source periodically for updates.

 

From (you guessed right) Wikipedia

real-time web

The real-time web is a network web using technologies and practices that enable users to receive information as soon as it is published by its authors, rather than requiring that they or their software check a source periodically for updates.

 

From (you guessed right) Wikipedia

real-time web

Examples of real-time web are Facebook's newsfeed, and Twitter, implemented in social networking, search, and news sites. Benefits are said to include increased user engagement ("flow") and decreased server loads.

 

From (you guessed right) Wikipedia

real-time web

Examples of real-time web are Facebook's newsfeed, and Twitter, implemented in social networking, search, and news sites. Benefits are said to include increased user engagement ("flow") and decreased server loads.

 

From (you guessed right) Wikipedia

channels

Developer-friendly asynchrony for Django

channels

Channels is a project to make Django able to handle more than just plain HTTP requests, including WebSockets and HTTP2, as well as the ability to run code after a response has been sent for things like thumbnailing or background calculation.

Concepts
Channel

Ordered, first-in first-out queue with message expiry and at-most-once delivery to only one listener at a time

Think task queues or go channels

Network-transparent

Concepts
Consumer

Routing maps channels to consumers
 

For every message on the channel, Django will call the corresponding consumer function with a message object

Messages also contain a reply channel

Concepts
GROUP

Channels only deliver to a single listener, they can’t do broadcast
 

Groups allow easy control of a set of reply channels
 

It abstracts member tracking, broadcasting and expiry
 

Layers
Interface Server

Takes client requests and loads the into  the channels system
 

Daphne is an ASGI server that can handle HTTP, HTTP2 and WebSocket protocol

Layers
Worker Server

Runs consumer functions
 

Configurable to be limited to some channels
 

Layers
Channel Backend

Communication layer between interface and worker servers
 

Current implementations include in-memory, redis and IPC
 

#THROWBACKTHURSDAY

Raise your ✋ if you know what a SHOUTBOX is

Let's make one USING CHANNELS!

Install channels

INSTALLED_APPS = [
     'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
+    'channels',
]

+CHANNEL_LAYERS = {
+    'default': {
+        'BACKEND': 'asgiref.inmemory.ChannelLayer',
+        'ROUTING': 'app.routing.channel_routing',
+    },
+}

channels_shoutbox/settings.py

+from app.consumers import ShoutboxConsumer
+from channels.routing import route_class
+
+channel_routing = [
+    route_class(ShoutboxConsumer, path=r"^/shoutbox"),
+]
+

app/routing.py

+from channels.generic.websockets import JsonWebsocketConsumer
+
+
+class ShoutboxConsumer(JsonWebsocketConsumer):
+    pass

app/consumers.py

Old-School Django

+class Message(models.Model):
+    name = models.CharField(max_length=255)
+    text = models.TextField(blank=True)
+    sent_at = models.DateTimeField(auto_now_add=True)

app/models.py

+from app.models import Message
 from django.shortcuts import render
 
 
 def index(request):
-    return render(request, 'index.html')
+    messages = Message.objects.order_by('-sent_at').all()
+    return render(request, 'index.html', {'messages': messages})

app/views.py

Consumer

+from app.models import Message
 from channels.generic.websockets import JsonWebsocketConsumer
+from django.template.defaultfilters import date
 
 
 class ShoutboxConsumer(JsonWebsocketConsumer):
-    pass
+
+    def connection_groups(self):
+        return ['all']
+
+    def receive(self, content):
+        message = Message.objects.create(**content)
+        content.update(sent_at=date(message.sent_at, 'r'))
+        self.group_send('all', content)

app/consumers.py

Javascript

+var ws = new WebSocket('ws://' + location.host + '/shoutbox')
+var messages = $('.messages')
+var form = $('form')
+
+function sendMessage(e) {
+  e.preventDefault()
+  var name = form.name.value
+  if (!name) {
+    return
+  }
+  var text = form.text.value
+  ws.send(JSON.stringify({
+    name: name,
+    text: text,
+  }))
+  form.text.value = null
+}
+
+function recvMessage(e) {
+  var data = JSON.parse(e.data)
+  // Render message in DOM
+}
+
+form._.events({submit: sendMessage})
+ws.onmessage = recvMessage

app/static/app.js

Demo

Source

Questions?

For announcements join our Facebook group:

https://www.facebook.com/groups/pythonph/

 

For meetup news join our Meetup page:
https://www.meetup.com/pythonph/

Made with Slides.com