Hey, check out my latest project: Kouio RSS reader

Real-time Web Apps with Django and WebSockets

August 13, 2011  /  Home

I recently took part in Django Dash, the annual hackathon where teams of up to three compete to build the best Django project they can within 48 hours. This year I worked with Travis White and Josh de Blank to build DrawnBy - a collaborative drawing application that allows people to sketch together in real-time.

DrawnBy makes extensive use of WebSockets which are mostly unheard of in web stacks like Django and Rails, or even antiquated stacks like PHP and ASP.NET, which are all designed around accepting a request and returning a response to the browser as quickly as possible. This is in contrast to WebSockets which allow full duplex communication between the browser and server, and therefore require long running requests per user.

A variety of patterns for dealing with WebSockets in Django emerged while developing DrawnBy, and since the Dash I’ve been working on abstracting these into a reusable Django application called django-socketio which I’ve released today. It’s available on GitHub, Bitbucket and PyPI.

Here’s an overview of the features.

Features

Channels

The WebSocket implemented by gevent-websocket provides two methods for sending data to other clients, socket.send which sends data to the given socket instance, and socket.broadcast which sends data to all socket instances other than itself.

A common requirement for WebSocket based applications is to divide communications up into separate channels. For example a chat site may have multiple chat rooms and rather than using broadcast which would send a chat message to all chat rooms, each room would need a reference to each of the connected sockets so that send can be called on each socket when a new message arrives for that room.

django-socketio extends Socket.IO both on the client and server to provide channels that can be subscribed and broadcast to.

To subscribe to a channel client-side in JavaScript use the socket.subscribe method:

var socket = new io.Socket();
socket.connect();
socket.on(connect, function() {
    socket.subscribe(my channel);
});

Once the socket is subscribed to a channel, you can then broadcast to the channel server-side in Python using the socket.broadcast_channel method:

socket.broadcast_channel("my message")

Events

The django_socketio.events module provides a handful of events that can be subscribed to, very much like connecting receiver functions to Django signals. Each of these events are raised throughout the relevant stages of a Socket.IO request.

Events are subscribed to by applying each event as a decorator to your event handler functions:

from django_socketio.events import on_message

@on_message
def my_message_handler(request, socket, context, message):
    ...

Each event handler takes at least three arguments: the current Django request, the Socket.IO socket the event occurred for, and a context, which is simply a dictionary that can be used to persist variables across all events throughout the life-cycle of a single WebSocket connection.

Like Django signals, event handlers can be defined anywhere so long as they end up being imported. Consider adding them to their own module that gets imported by your urlconf, or even adding them to your views module since they’re conceptually similar to views.

Binding Events to Channels

All events other than the on_connect event can also be bound to particular channels by passing a channel argument to the event decorator. The channel argument can contain a regular expression pattern used to match again multiple channels of similar function.

For example, suppose you implemented a chat site with multiple rooms. WebSockets would be the basis for users communicating within each chat room, however you may want to use them elsewhere throughout the site for different purposes, perhaps for a real-time admin dashboard. In this case there would be two distinct WebSocket uses, with the chat rooms each requiring their own individual channels.

Suppose each chat room user subscribes to a channel client-side using the room’s ID:

var socket = new io.Socket();
var roomID = 42;
socket.connect();
socket.on(connect, function() {
    socket.subscribe(room- + roomID);
});

Then server-side the different message handlers are bound to each type of channel:

@on_message(channel="dashboard")
def my_dashboard_handler(request, socket, context, message):
    ...

@on_message(channel="^room-")
def my_chat_handler(request, socket, context, message):
    ...

Chat Demo

The “hello world” of WebSocket applications is naturally the chat room. As such django-socketio comes with a demo chat application that provides examples of the different events and channel features available.

Keywords  /  Home