Intermission: using Twisted to serve Django via wsgi

and now, while on break from our ongoing tutorial (table of contents).

are you tired of starting django and twisted via two separate processes, possibly having them run in two different shells for debugging purposes? i know i am.

fortunately, django and twisted can inter-operate – Django supports the Python wsgi interface, and twisted can be used to serve wsgi applications. this is actually the ideal method for having the two interact with each other (and is the first post in this tutorial that runs 100% counter to my disclaimer)

detailed documentation for this process is available at the twisted documentation site http://twistedmatrix.com/documents/current/web/howto/web-in-60/wsgi.html

give a shot to modifying your current tutorial code to make this work.


there are two components required to properly serve the django portions of this tutorial as wsgi resources:

  • configuring Django to work as twisted wsgi resource
  • serving the static components of django through twisted

this is slightly more complicated than it sounds, since, in newer versions, django dynamically searches through an ordered list of directories when serving its static components. using twisted to mimic that same functionality can take some work. i opted to use a custom directory scanning twisted resource

there aren’t very many decisions to make here; most of the work has to do with reading the documentation, and figuring out how to access the specific, relevant interfaces. if you’re stuck, you can find a clear example here; this basic resource might help, too.

 


tried it? given up? since i’ll be relying on this work when expanding the tutorial, it might be worth your time to see how i did it (and see if it makes sense to you). take a look at this commit/diff to get a list of the specific changes i made – let me know if it makes sense (or if i made a mistake/have a typo)


the git repository for the ongoing chat tutorial has this completed step tagged as v.0.2.1 – if you’ve already cloned the git repo, you can check out a clean version like so:

git checkout v0.2.1

whyfore chat, with django, twisted and websockets?

Aside

upon seeing the work i’ve put into writing tutorials, showing how to get realtime chat working in django + twisted/websockets, you might make the assumption that i consider this architecture to be, in general, a good idea.

A.

twisted’s implementation of websockets is, as of this writing, not integrated into the main branch.

don’t use code that isn’t considered, by its authors, to be reliable enough to merge into and release as part of their application distribution.

B.

twisted is an event-driven networking engine
django is a solid, easy to use web framework
websockets, a tcp based protocol, is usually implemented as a strange mix between the tcp and http protocols

it is, generally speaking, not a good idea to mix abstraction levels; adding event-driven components to your application by combining twisted and django is a bad architectural decision. I strongly suggest you consider using twisted.web instead of mixing django and twisted.

websockets are a strange mix of protocols, and can be difficult to work with unless you are very careful with your choice of libraries and application design, scope and implementation. at the time of this post, i would recommend against using websockets, in production, with the standard deployment of twisted. i strongly urge you to consider the following alternatives, in rough order of likelihood to work for you:

 


TUTORIAL: Real-time chat with Django, Twisted and WebSockets – Part 3

[Part 1] - [Part 2] - [Part 3] - [Addendums] - [Source]
[Table of Contents]

Preamble

You might wonder – why add long-polling client, connecting to a http service, to a functional websocket client-server implementation? The quick answer:

  • A lot of people seem to want to know how to implement long polling clients against a twisted web server
  • It’s a useful skill to have in your toolbelt
  • The interaction between a blocking service (ie, one handling http requests, where long-polling might be used to emulate a continuous connection) and a non blocking service (ie, the websocket chat protocol) can be interesting to get right.
  • Websockets are the future, but not (yet) the now

Django components

We’ll reuse the server we finished in Part 2, and have it serve a chat room interface which connects to our server with an http connection, relying on long-polling to emulate a continuous, “real-time” connection.

First, add an entry to your chat/urls.py file, telling Django we where we want it to serve our view:

urlpatterns = patterns('',
        url(r'^$', views.index, name='index'),
        url(r'^(?P<chat_room_id>\d+)/$', views.chat_room, name='chat_room'),
        url(r'^long_poll/(?P<chat_room_id>\d+)/$', views.longpoll_chat_room, name='longpoll_chat_room'),
)

Next, create the relevant view in chat/views.py:

def longpoll_chat_room(request, chat_room_id):
  chat = get_object_or_404(ChatRoom, pk=chat_room_id)
  return render(request, 'chats/longpoll_chat_room.html', {'chat': chat})

You can see from the view definition, that we’re going to be using a template (since we’re passing a template reference into the render call), and, since the template reference is a path – ‘chats/longpoll_chat_room.html’ – that’s where Django is going to expect it to be on disk. So, go ahead and open up chat/templates/chats/longpoll_chat_room.html, and write the following chunk in it:

{% load staticfiles %}</pre>
<h1>{{ chat.name }}</h1>
<div id="message_list"><ul></ul></div>

You’ll notice that this file is almost identical to the websocket chat. The reference to the graceful.webSocket library isn’t here, since it’s not needed, and all of the javascript client connection work is now going to be in a separate file called (instead of inside of a <script></script> tag like the chat_room.html implementation).

So, let’s create the javascript client, then. Edit chat/static/long_poll.js file, and write the following code into it:

//Numeric representation of the last time we received a message from the server
var lastupdate = -1;

$(document).ready(function(){
    getData();

    var inputBox = document.getElementById("inputbox");

    inputbox.addEventListener("keydown", function(e) {
      if (!e) { var e = window.event; }

      if (e.keyCode == 13) {
        e.preventDefault(); // sometimes useful
        postData(inputbox.value);
        inputbox.value="";
      }
    }, false);

});

var getData = function() {
    $.ajax({
        type: "GET",
        // set the destination for the query
        url: 'http://127.0.0.1:1025?lastupdate='+lastupdate+'&callback=?',
        dataType: 'jsonp',
        // needs to be set to true to avoid browser loading icons
        async: true,
        cache: false,
        timeout:1000,
        // process a successful response
        success: function(response) {
            // append the message list with the new message
            var message = response.data;
            $("#message_list ul")
                .prepend($('<li>'+message+'</li>'));
            // set lastupdate
            lastupdate = response.timestamp;
         },
         complete: getData(lastupdate),
    });
};

var postData = function(data) {
   $.ajax({
        type: "POST",
        // set the destination for the query
        url: 'http://127.0.0.1:1025',
        data: {new_message: data},
        // needs to be set to true to avoid browser loading icons
        async: true,
        cache: false,
   });
}

There’s a lot going on here.

We first add an almost-identical looking event listener to the one in the websocket based chat, to our input box, telling it to send messages when a user presses return/enter.

We then define two functions – getData and postData – which handle the actual communication with the chat server.

postData is the simpler of the two – it uses functionality defined by jQuery ($.ajax) to build, and then send, a post request to our chat server, with the contents of a message as the only argument. You can read the documentation for that command to learn more about how it does what it does. Note that we labeled sent information as  “new_message” – the server-side api component is going to have to unpack that by correctly referring to new_message when it’s received.

The more complex function is the one relying on long-polling to simulate real-time communication – getData. We’re doing a few interesting things here: First, we set the dataType to ‘jsonp’. This is necessary, since the javascript file is served by Django on one port (8000), and the chat interface is served by twisted on another (1025). When successful (when the server responds with a new message for us), we perform the same basic function as the websockets message receive function did – we add the message to our chat room.

The “long-polling” component is implemented by setting a function to execute on “complete”, and setting the timeout variable. With a timeout of 1000, we’re instructing jQuery to give the server at least 1 second to respond to our call. If either the server or at least 1 second has gone by, jQuery will terminate the request and call the complete function.

This completes the long-polling loop: once every second, we open a server connection asking it “do you have any new messages for me?”, handling any messages as they come.

For a production level client, 1 second is probably not appropriate – probably we’d want an exponentially decaying time delay, to be more efficient in network use. For now, this’ll do though.

Now we have a functional long-polling chat client. You can test it by starting Django if it’s not yet running, and opening up one of your chat rooms like so:

http://127.0.0.1:8000/long_poll/1

Since the chat-server components aren’t implemented yet, you might see javascript connection errors in your browser’s console, and actual message sending/receiving won’t (quite) work. So, let’s fix that:

Twisted based Blocking (http) chat server

We’re going to perform some delicate bits of surgery on the existing twisted chat server, to add a in a second, blocking, http-based, chat protocol. We’ll also want the two protocols to share data – so that they’ll provide a single set of chat rooms for people to connect to.

So. Open up your twisted server file (chatserver.py) and edit it to add the following to the top:

from twisted.web.websockets import WebSocketsResource, WebSocketsProtocol, lookupProtocolForFactory

import time, datetime, json, thread
from twisted.web.resource import Resource
from twisted.internet import task
from twisted.web.server import NOT_DONE_YET

These are references to the libraries and functions we’re going to be using. Next, we’ll define the chat protocol. Insert the following chunk into the file (replacing the existing ChatFactory definition):

from twisted.internet.protocol import Factory
class ChatFactory(Factory):
    protocol = WebsocketChat
    clients = []
    messages = {}

class HttpChat(Resource):
    #optimization
    isLeaf = True
    def __init__(self):
        # throttle in seconds to check app for new data
        self.throttle = 1
        # define a list to store client requests
        self.delayed_requests = []
        self.messages = {}

        #instantiate a ChatFactory, for generating the websocket protocols
        self.wsFactory = ChatFactory()

        # setup a loop to process delayed requests
        # not strictly neccessary, but a useful optimization,
        # since it can force dropped connections to close, etc...
        loopingCall = task.LoopingCall(self.processDelayedRequests)
        loopingCall.start(self.throttle, False)

        #share the list of messages between the factories of the two protocols
        self.wsFactory.messages = self.messages
        # initialize parent
        Resource.__init__(self)

    def render_POST(self, request):
        request.setHeader('Content-Type', 'application/json')
        args = request.args
        if 'new_message' in args:
            self.messages[float(time.time())] = args['new_message'][0]
            if len(self.wsFactory.clients) > 0:
                self.wsFactory.clients[0].updateClients(args['new_message'][0])
            self.processDelayedRequests()
        return ''

    def render_GET(self, request):
        request.setHeader('Content-Type', 'application/json')
        args = request.args

        if 'callback' in args:
            request.jsonpcallback =  args['callback'][0]

        if 'lastupdate' in args:
            request.lastupdate =  float(args['lastupdate'][0])
        else:
            request.lastupdate = 0.0

        if request.lastupdate < 0:
            return self.__format_response(request, 1, "connected...", timestamp=0.0)

        #get the next message for this user
        data = self.getData(request)

        if data:
            return self.__format_response(request, 1, data.message, timestamp=data.published_at)

        self.delayed_requests.append(request)
        return NOT_DONE_YET

    #returns the next sequential message,
    #and the time it was received at
    def getData(self, request):
        for published_at in sorted(self.messages):
            if published_at > request.lastupdate:
                return type('obj', (object,), {'published_at' : published_at, "message": self.messages[published_at]})();
        return

    def processDelayedRequests(self):
        for request in self.delayed_requests:
            data = self.getData(request)

            if data:
                try:
                    request.write(self.__format_response(request, 1, data.message, data.published_at))
                    request.finish()
                except:
                    print 'connection lost before complete.'
                finally:
                    self.delayed_requests.remove(request)

    def __format_response(self, request, status, data, timestamp=float(time.time())):
        response = json.dumps({'status':status,'timestamp': timestamp, 'data':data})

        if hasattr(request, 'jsonpcallback'):
            return request.jsonpcallback+'('+response+')'
        else:
            return response

There is a lot going on here.

Let’s break it down some. We’ve added a “messages” structure to ChatFactory(); this structure is going to function as a shared repository for all of the messages we receive over both protocols – to make it possible for users at either protocol to see the same contents for a chat room.

Beyond the standard setup, the initialization function (__init__(self)) instantiates a ChatFactory(), and creates a reference to its messages, so that the two protocols now access the same list of messages, and effectively share a chat room. We also have the initialization function start a loop that runs the processDelayedRequests function once a second. This is not strictly necessary for sending out messages – as you’ll see when you read render_GET – but it helps optimize the use of server resources, since, besides sending out messages as quickly as possible after they’re received, it also has the side effect of freeing up resources dedicated to dropped connections.

We define a render_POST function. The function name conforms to twisted conventions – twisted will attempt to call a function by this name every time a HTTP POST request comes in. Since we know that only message sends perform posts for now, we assume that we’re receiving a message, and go ahead and process it.

First, we add a message to our list of messages. Then, we send the message out to all of the websocket based clients by calling the (soon to be implemented) updateClients method on the first websocket client we can find.

Finally, we call processDelayedRequests, to update any waiting httpclients with the new message.

We also define a render_GET function. This function responds to requests to new messages. Since the initial request is going to have a lastupdate time of -1 (this is hard-coded in the long_polling.js client), we check if the lastupdate is below 0, and, if it is, we send out a message to let the user know he’s connected, and to request updates at time 0 or higher.

We then check to see if there’s any data waiting, which this user should see – the getData function gets the next message that this user should see in his chat room; if there is a message for this user to see, we send it out, together with the time it was received at (so the user knows to ask for the next message in the sequence next time).

This creates a request-loop, with the user requesting each message, one by one, until he’s up to date with the chat. Note that in a production application, you’ll probably want to send messages back to the user in batches (since creating/closing http connections is a an inefficient use of network and server resources).

Once we’ve run out of messages to send out to the user, we append the request to a list of clients waiting for an update, and use the twisted shorthand NOT_DONE_YET to ensure that the connection is not closed when the render_GET function returns (twisted, by default, closes the http connection if we return any other value from this function). processDelayedRequests performs much the same function as the render_GET function, only it performs it for requests currently waiting for an update.

Once a message is sent out, the related connection is closed with a request.finish() function call, and all server resources allocated to it are freed as a side effect of removing it from the delayedRequest list. getData and __format_response are helper functions, which are fairly readable. Note that getData is dynamically creating/instantiating a python object from constructed text (the syntax is a bit weird, but I like being able to do this in python, so I take any excuse to teach people that it’s possible).

We should probably also update our websocket based chat protocol, and have it send messages out to any of the http/blocking based clients. To do that, replace the existing dataReceived function with the following two (we’re factoring updateClients out of dataReceived to make it easier to call it from the POST function we wrote above):

    def dataReceived(self, data):
        self.factory.messages[float(time.time())] = data
        self.updateClients(data)

    def updateClients(self, data):
        for c in self.factory.clients:
            c.message(data)

Next, we’ll have to tell twisted that we’re now running two resources, on two different ports, and give it some directions on how to construct its infrastructure for supporting them. We’ll do that by replacing the last bit in the file with the following:

#resource = WebSocketsResource(lookupProtocolForFactory(ChatFactory())) #this line can be removed

from twisted.web.resource import Resource
from twisted.web.server import Site

from twisted.internet import protocol
from twisted.application import service, internet

resource = HttpChat()
factory = Site(resource)
ws_resource = WebSocketsResource(lookupProtocolForFactory(resource.wsFactory))
root = Resource()
root.putChild("",resource) #the http protocol is up at /
root.putChild("ws",ws_resource) #the websocket protocol is at /ws
application = service.Application("chatserver")
internet.TCPServer(1025, Site(root)).setServiceParent(application)

We now have the infrastructure for a twisted webserver running two chat protocols – one http based one at http://127.0.0.1:1025/, and another which is websocket based at http://127.0.0.1:1025/ws. Let’s test and see if things work! Restart twisted if it’s running, and make sure Django is still up (if not, start it):

bash: python manage.py runserver &
bash: twistd -n -y chatserver.py

Once that’s done, in a flurry of keystrokes, open up two browser windows here on one of your long_polling chat rooms, say http://127.0.0.1:8000/long_poll/1, and another two windows on the websocket client for the same window: http://127.0.0.1:8000/chat_room/1 – and chat away!

Source code

I’ve posted the source code for this tutorial to a git repository. Get the version up to this point here, or by running the following at a command line (explore the git repo for other work I’ve done beyond this tutorial):

git clone https://github.com/aausch/django_twisted_chat.git
git checkout tags/v0.1.2

What next?
You might notice a few things still need doing, for this chat room system to work – you can try to implement fixes yourself. In rough increasing order of difficulty:

  • try creating two chat rooms, and posting some messages into each. What happens? Warning: when trying to fix this problem, avoid trying to get the twisted and the Django server to communicate directly.
  • there are delays in some of the updates to the long_poll version of the chat rooms. Since things are running locally, the delays don’t have to be as long as they are (some of them don’t really have to be there at all!). As an exercise, try removing/reducing the delays
  • the websocket chat rooms don’t update chat history on disconnect (if you close a window and you open it, you won’t get back-chat history). If you want a fun next exercise, try adding that in!
  • messages don’t persist, if the server goes down (and take up more and more memory the longer the server is up for!). Try modifying your code to write messages to the database, as they are received, and only store a limited (fixed) number of them in memory at any point in time
  • if you send a lot of messages, quickly, in a websocket client, they won’t all make it over to the http/long polling clients [correction: it’s almost certain you won’t notice messages being lost, on account of the GIL]. Oh no! There’s a race condition somewhere in the system. Fix this bug! (solution)

TUTORIAL: Real-time chat with Django, Twisted and WebSockets – Part 2

[Part 1] - [Part 2] - [Part 3] - [Addendums] - [Source]
[Table of Contents]

Thus far, we have a very basic UI framework for a non-functional chat system, served by Django. We’ll now implement a chat server, chat client, and api, with the chat functionality being managed and served by Twisted via websockets.

Install Twisted with Websockets 

I used this git branch of the Twisted project, as it’s the most up-to-date as of the time of this writing. The specific steps you’ll have to take to install it depend on your architecture, but you’ll probably want to run commands similar to these ones:

#checkout the twisted project
git clone https://github.com/twisted/twisted.git twisted-websocket

#switch to the most up to date websocket branch
cd twisted-websocket
git fetch
git checkout websocket-4173-4

#install it - preferably do this in a virtualenv
python setup.py install

Write a web socket chat server for twisted

Create a file to store the chat server code, say, chatserver.py, and write the following code into it:

from twisted.protocols import basic
from twisted.web.websockets import WebSocketsResource, WebSocketsProtocol, lookupProtocolForFactory

#basic protocol/api for handling realtime chat
class MyChat(basic.LineReceiver):
    def connectionMade(self):
        print "Got new client!"
        self.transport.write('connected ....\n')
        self.factory.clients.append(self)

    def connectionLost(self, reason):
        print "Lost a client!"
        self.factory.clients.remove(self)

    def dataReceived(self, data):
        print "received", repr(data)
        for c in self.factory.clients:
            c.message(data)

    def message(self, message):
        self.transport.write(message + '\n')

from twisted.web.resource import Resource
from twisted.web.server import Site
from twisted.internet import protocol
from twisted.application import service, internet

#Create a protocol factory
#The factory is usually a singleton, and
#all instantiated protocols should have a reference to it,
#so we'll use it to store shared state
#(the list of currently connected clients)
from twisted.internet.protocol import Factory
class ChatFactory(Factory):
protocol = MyChat
clients = []

resource = WebSocketsResource(lookupProtocolForFactory(ChatFactory()))
root = Resource()
#serve chat protocol on /ws
root.putChild("ws",resource)

application = service.Application("chatserver")
#run a TCP server on port 1025, serving the chat protocol.
internet.TCPServer(1025, Site(root)).setServiceParent(application)

The code above follows one of the twisted code samples/tutorials, implementing a basic telnet chat server, and modifies it to use the web socket protocol and related infrastructure, instead of the basic tcpip one. Note that this example is still using a wrapper around the basic.lineReceiver protocol – nothing too fancy.

Since, a few steps from now, we’re going to want to serve more than one protocol (one for web sockets, and the other for http/long poll requests), the code is explicitly creating a Site(), and building a WebSocketResource/ChatFactory to deploy (as opposed to relying on ServerFactory() to build one for us). The websockets api is being served at http://127.0.0.1:8000/ws/

This is the extent of work required to get a very basic twisted server working. Now, try running it:

bash: twistd -n -y chatserver.py

If the Django server is running, also, you should now be able to connect to

http://127.0.0.1:8000/chats/1

see the chat room, and, well, still not be able to properly send messages back and forth, since we don’t yet have a client-side component connecting to our server over websockets, and handling that side of the communication. So let’s do that next:

Download and install a javascript web socket client library

Not all browsers implement websockets, and not all websockets implementations are equivalent, so it’s difficult to write code which is portable. It’s easier to use a library for handling this kind of work – I used this one: https://jquery-graceful-websocket.googlecode.com/files/jquery.gracefulWebSocket.js

Download the file, and place it under chat/static/. (You might have to create the directory):

bash: mkdir chat/static
bash: cp ~/Downloads/jquery.gracefulWebSocket.js chat/static/

Implement client-side web socket logic:

We’re already serving the chat-room scaffolding through a template. One way to provide the clientside components for a websockets connection is through javascript embedded directly in the source html file. So let’s do that – edit chat/templates/chats/chat_room.html, and add the following to the header section:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript" src="{% static "jquery.gracefulWebSocket.js" %}"></script>
<script>
    $(document).ready( function() {
    window.chat = {};
    //Instantiate a websocket client connected to our server
    chat.ws = $.gracefulWebSocket("ws://127.0.0.1:1025/ws");

    //Basic message send
    chat.send = function (message) {
      chat.ws.send(message);
    }

    //Basic message receive
    chat.ws.onmessage = function (event) {
      var messageFromServer = event.data;
      var list_element = document.createElement('li');
      list_element.innerHTML = messageFromServer;
      $("#message_list ul").prepend(list_element);
    };

    var inputBox = document.getElementById("inputbox");

    inputbox.addEventListener("keydown", function(e) {
      if (!e) { var e = window.event; }

      //keyCode 13 is the enter/return button keyCode
      if (e.keyCode == 13) {
        // enter/return probably starts a new line by default
        e.preventDefault();
        chat.send(inputbox.value);
        inputbox.value="";
      }
    }, false); });
</script>

The script first includes jQuery, which the gracefulWebsocket library relies on. The order of inclusion matters, so don’t switch those lines around. It then defines a namespace for our chat functionality (window.chat), and instantiates a gracefulWebsocket in the namespace, configuring it to connect to our web socket chat server:

chat.ws = $.gracefulWebSocket("ws://127.0.0.1:1025/ws");

We need two functions, let’s call them chat.send(), for sending messages out, and chat.onmessage() for handling messages received from the server – the code is fairly readable. We’re also adding an event listener to our input text box, sending out a message each time the user presses return.

Make sure you’ve saved the file, and, voila, you should now have a working, very basic, web-socket based chat server. The static/html content served by Django, and a websockets based chat api being served/managed by twisted.

Django is smart enough to know to pick up changes to its templates, so all of this should now work, without requiring a restart of the server. Just reload the two windows you have opened on http://127.0.0.1/chats/1 (or go back to http://127.0.0.1/chats/, and open two browser windows on the same chat room) – and enjoy sending messages, in real time, from one window to the next.

Next: add a http api for your chat rooms, and a long-polling based client to connect to them.

TUTORIAL: Real-time chat with Django, Twisted and WebSockets – Part 1

[Part 1] - [Part 2] - [Part 3] - [Addendums] - [Source]
[Table of Contents]

(I make some basic assumptions about the readers of this tutorial).

Overview

We’re going to be building a very basic chat server on top of Django 1.5, and Twisted 13.1. For this implementation, we’ll be relying on Django, and Django’s html templating facility, to design, build  and to serve the ui components for the application.

Twisted will provide the actual chat functionality, over two separate channels:

  • first, we’ll have twisted serving chat to websocket connections
  • we’ll then add an http api, to demonstrate what a long-polling chat client might look like

if you haven’t already, it’s probably worth spending a few minutes making sure you have gitpipvirtualenv and virtualenvwrapper installed and working properly, and then set up an environment using Django 1.5.

Django scaffolding

create a new Django project:

bash: django-admin.py startproject django_twisted_chat

test that the project is up and running properly by starting the server:

bash: cd django_twisted_chat
bash: python manage.py runserver

and then connecting to http://127.0.0.1:8000/. You can check that the server is running – you’ll see a “Welcome to Django” page if it’s running properly.

Configure Django Project

edit django_twisted_chat/settings.py and add the following lines to the top:

#convenience variable, to refer to the directory the application is currently running in:
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

configure a database; since this is a demo, and is unlikely to ever make it to production, we’ll use sqllite and store our data to disk:

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
        'NAME': './sqlite_database_file',
         # The following settings are not used with sqlite3:

we’re going to be serving some static files in this demo, javascript files only for now (a more advanced version may serve images, and other static elements too), so we should tell Django where we’re storing these files. I used the default – a  “static” directory, off the base of the server and/or individual applications:

# Additional locations of static files
STATICFILES_DIRS = (
# Put strings here, like "/home/html/static" or "C:/www/django/static".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
    os.path.join(BASE_DIR, "static"),
)

We’ll also use Django’s templating facilities, so configure the server to use them:

TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
os.path.join(BASE_DIR, 'templates'),
)

In a few minutes we’ll make our chat application, and we can reuse Django’s admin interface as a shortcut interface, for manually adding a few required elements to the database (ie., chat room definitions). So, let’s configure the server to serve both of those:

INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
'chat',
)

Make sure to save your settings file.

Set up url paths

Now, well want the Django project to properly direct http requests to our chat application, as well as to the built in admin components. To do that, edit the django_twisted_chat/urls.py file, like so:

from django.conf.urls import patterns, include, url
from django.views.generic import RedirectView
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
     url(r'^(/)?$', RedirectView.as_view(url='/chats/')),
     url(r'^chats/', include('chat.urls')),
     url(r'^admin/', include(admin.site.urls)),
)

Most of this should be familiar from the Django tutorial. The only potential addition is the use of the RedirectView.as_view shorthand. This is a built in shorthand tool for redirecting requests from one url to the other; we’re using it to redirect all requests for http://127.0.0.1 and/or http://127.0.0.1/ to http://127.0.0.1/chats/. We’ll using it here for convenience’s sake – since we’re not going to be serving anything on http://127.0.0.1/ for now, have the server automatically redirect requests to the /chats/ application instead of returning a 404 error.

Create and configure Django Chat application

Create a Django application to serve the chat specific components:

bash: python manage.py startapp chat

for now, we’ll only worry about adding the basic components for a web-sockets based chat client. Django will serve the ui and javascript source portions, letting Twisted handle the chat api and server components. First, construct the model for a chat room -edit chat/models.py:

from django.db import models

class ChatRoom(models.Model):
    name = models.CharField(max_length=200)

    def __unicode__(self):
	return self.name

We're building a model for storing a ChatRoom. Note that this specific tutorial doesn't go so far as to store actual chat messages to disk, so we won't need a ChatMessage model (though that model is probably present in the sample code provided at the end of this sequence). That's work for a future followup.

Make sure that we have a nice UI for editing the room of existing chat rooms. For now, we'll rely on the existing admin console for Django, so, create and edit the chat/admin.py file:

from django.contrib import admin
from chat.models import ChatRoom

admin.site.register(ChatRoom)

Next, we'll define the application specific urls, in chat/urls.py:

from django.conf.urls import patterns, url

from chat import views

urlpatterns = patterns('',
        url(r'^$', views.index, name='index'),
        url(r'^(?P\d+)/$', views.chat_room, name='chat_room'),
)

This is where the project urls file (django_twisted_chat/urls.py) is going to look for urls (a result of the "url(r'^chats/', include('chat.urls'))" line we wrote in there earlier).

Now, we'll need a couple of html templates for displaying chat room related information. The templates need somewhere to sit on disk, so create an app specific templates directory:

bash: mkdir chat/templates

then, create a templates directory for the url we'll be using (we called it chats in the django_twisted_chat/urls.py file, so this directory structure is what Django will, by default, expect):

bash: mkdir chat/templates/chats

We'll have our index list all available chat rooms, so create a template for that. Edit chat/templates/chats/index.html:

{% if chat_list %}</pre>
<ul>
<ul>{% for chat in chat_list %}</ul>
</ul>
<ul>
<ul>
	<li><a id="" href="{% url 'chat_room' chat.id %}"> {{ chat.name }}</a></li>
</ul>
</ul>
<ul>{% endfor %}</ul>
<pre>



{% else %}

No chats are available.

{% endif %}

Then create a page to serve as ui for an individual chat room. Edit chat/templates/chats/chat_room.html:

{% load staticfiles %}</pre>
<h1>{{ chat.name }}</h1>
<div id="message_list"></div>
<pre>



Next, we can construct the relevant views. Edit the chat/views.py file:

from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404

from chat.models import ChatRoom

def index(request):
  chat_rooms = ChatRoom.objects.order_by('name')[:5]
  context = {
    'chat_list': chat_rooms,
  }
  return render(request,'chats/index.html', context)

def chat_room(request, chat_room_id):
  chat = get_object_or_404(ChatRoom, pk=chat_room_id)
  return render(request, 'chats/chat_room.html', {'chat': chat})

Again, this follows the basic pattern taught in the Django tutorial. The convenience method get_object_or_404 may be unfamiliar - it's a shorthand for searching for an element in the database, and automatically returning a 404 not found error if the object is missing (in this case, if someone accidentally tries to access a chat room that doesn't yet exist).

This should now give us a functional Django application. Let's make sure that it works properly, and that we don't have any typos. Create and define the structure of the database (make a super user for it when you create it), then start Django:

bash: python manage.py syncdb
bash: python manage.py runserver

Next, log in to the admin console and create a chat room. Go to:

http://127.0.0.1:8000/admin/

log in with the super user that you created, and add a ChatRoom (give it a name and save it).

To test that the chat room exists and everything we've done so far is working properly, go to:

http://127.0.0.1:8000/

You should automatically be redirected to http://127.0.0.1:8000/chats/, and see a list of existing chat rooms.

Chat Room List

Click on one of them, and you should now see an empty chat room on your screen:

Empty chat room

For fun, try opening the chat room url in multiple browser windows, and then typing in it. Unfortunately, nothing interesting happens yet. That's where the Twisted chat server comes in - see Part 2

TUTORIAL: Real-time chat with Django, Twisted and WebSockets – Prerequisites

If you came directly here, you can probably skip to Part 1.

I’m assuming a basic competency writing Python code, as well as access to a development environment, editor, and debug/test environment configured for work with Python (with possible extensions for working with Django).

If new at working with Python, you can do worse than using Eclipse’s pydev setup, though you’ll be fine working with any text editor.

This is not the place to learn about Django. Completing and understanding the Django tutorial is worthwhile, since I write code that’s very close to the code written there, in this tutorial. (If you’ve completed it before but are still shaky on Django architecture and terminology, it’s worth your rereading the tutorial, and re-completing the first 4 stages before continuing)

Twisted is a bit harder to get into. For the minimalist, read twisted in 60 seconds, and then take a look at relevant example 1 and example 2, and understand how they work.

Continue to Part 1.