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

24 thoughts on “TUTORIAL: Real-time chat with Django, Twisted and WebSockets – Part 1

  1. Hi,

    I am getting the following error when trying to access the http://127.0.0.1:8000/admin/ when trying to create a chat room in part 1.
    Step:
    Next, log in to the admin console and create a chat room. Go to:

    http://127.0.0.1:8000/admin/
    ———————————————————————————————————————–
    ImproperlyConfigured at /admin/

    You’re using the Django “sites framework” without having set the SITE_ID setting. Create a site in your database and set the SITE_ID setting to fix this error.

    Request Method: GET
    Request URL: http://127.0.0.1:8000/admin/
    Django Version: 1.6
    Exception Type: ImproperlyConfigured
    Exception Value:

    You’re using the Django “sites framework” without having set the SITE_ID setting. Create a site in your database and set the SITE_ID setting to fix this error.

    Exception Location: /usr/local/lib/python2.7/dist-packages/django/contrib/sites/models.py in get_current, line 43
    Python Executable: /usr/bin/python
    Python Version: 2.7.3
    Python Path:

    [‘/home/bhakar/Desktop/Python-2.7.6/django_twisted_chat’,
    ‘/usr/lib/python2.7’,
    ‘/usr/lib/python2.7/plat-linux2’,
    ‘/usr/lib/python2.7/lib-tk’,
    ‘/usr/lib/python2.7/lib-old’,
    ‘/usr/lib/python2.7/lib-dynload’,
    ‘/usr/local/lib/python2.7/dist-packages’,
    ‘/usr/lib/python2.7/dist-packages’,
    ‘/usr/lib/python2.7/dist-packages/PIL’,
    ‘/usr/lib/python2.7/dist-packages/gst-0.10’,
    ‘/usr/lib/python2.7/dist-packages/gtk-2.0’,
    ‘/usr/lib/pymodules/python2.7’,
    ‘/usr/lib/python2.7/dist-packages/ubuntu-sso-client’,
    ‘/usr/lib/python2.7/dist-packages/ubuntuone-client’,
    ‘/usr/lib/python2.7/dist-packages/ubuntuone-control-panel’,
    ‘/usr/lib/python2.7/dist-packages/ubuntuone-couch’,
    ‘/usr/lib/python2.7/dist-packages/ubuntuone-installer’,
    ‘/usr/lib/python2.7/dist-packages/ubuntuone-storage-protocol’]

    Server time: Tue, 10 Dec 2013 02:37:39 +0000

      • Hi ferretfarmer,

        So, I was working on django 1.6 and hence I manually had to enable the sites framework. Thank you for the reply ! Now I’m running into another error. After creating two chatrooms through the admins site, I get the following :
        when I acess http://127.0.0.1:8000/chats/

        {{ chat.name }

        {{ chat.name }

        and clicking on any of the hyperlinks give an error page shown below:

        Page not found (404)
        Request Method: GET
        Request URL: http://127.0.0.1:8000/chats/%7B%

        Using the URLconf defined in django_twisted_chat.urls, Django tried these URL patterns, in this order:

        1. ^(/)?$
        2. ^chats/ ^$ [name=’index’]
        3. ^chats/ ^(?P\d+)/$ [name=’chat_room’]
        4. ^admin/

        The current URL, chats/{%, didn’t match any of these.

        • sounds like you’re not filling the templates correctly.

          are you still using django 1.6? if so, maybe the syntax for templates has changed between versions.

          otherwise, your problem is probably in one of the following three files – check that you’re using the correct format for urls (did you give the urls names?):
          https://github.com/aausch/django_twisted_chat/blob/master/chat/urls.py

          double check that the template file you’re using is in the correct format:
          https://github.com/aausch/django_twisted_chat/blob/master/chat/templates/chats/index.html

          and that your views are passing in the correct template arguments:
          https://github.com/aausch/django_twisted_chat/blob/master/chat/views.py

        • I am using Django 1.6 and I faced some errors cutting and pasting the code… I have changed ‘href=”{%”> {{ chat.name }’ for ‘href=”{{chat.id}}”> {{ chat.name }}’ in index.html and everything seems to work… well… I cannot see the same as in the last screenshot, but I think I will see it in the next part. Thanks for the tutorial!

            • hello please help me i m getting the following error again and again

              ValueError at /chats//
              invalid literal for int() with base 10: ”
              Request Method: GET
              Request URL: http://127.0.0.1:8000/chats//
              Django Version: 1.8.4
              Exception Type: ValueError
              Exception Value:
              invalid literal for int() with base 10: ”
              Exception Location: C:\Users\Rabiya Abdul Jabbar\Anaconda3\envs\dclass\lib\site-packages\django\db\models\fields\__init__.py in get_prep_value, line 985
              Python Executable: C:\Users\Rabiya Abdul Jabbar\Anaconda3\envs\dclass\python.exe
              Python Version: 3.4.4
              Python Path:
              [‘C:\\Users\\Rabiya Abdul Jabbar\\Anaconda3\\django_twisted_chat’,
              ‘C:\\Users\\Rabiya Abdul Jabbar\\Anaconda3\\envs\\dclass\\python34.zip’,
              ‘C:\\Users\\Rabiya Abdul Jabbar\\Anaconda3\\envs\\dclass\\DLLs’,
              ‘C:\\Users\\Rabiya Abdul Jabbar\\Anaconda3\\envs\\dclass\\lib’,
              ‘C:\\Users\\Rabiya Abdul Jabbar\\Anaconda3\\envs\\dclass’,
              ‘C:\\Users\\Rabiya Abdul Jabbar\\Anaconda3\\envs\\dclass\\lib\\site-packages’]
              Server time: Sat, 27 Feb 2016 17:29:36 +0300

  2. I am getting the following error

    need more than one value to unpack

    and showing the error in the views file

    return render(request,’chats/index.html’, context)

  3. Hi, I tried following this but at /chats it shows only this: {{ chat.name } on a bullet point, and when I click on it, it shows this:
    Page not found (404)
    Request Method: GET
    Request URL: http://127.0.0.1:8080/chats/%7B%
    Using the URLconf defined in django_twisted_chat.urls, Django tried these URL patterns, in this order:
    ^(/)?$
    ^chats/ ^$ [name=’index’]
    ^chats/ ^(?P\d+)/$ [name=’chat_room’]
    ^admin/
    The current URL, chats/{%, didn’t match any of these.

    Why is this happening?

    • hi an,

      looks like there might have been a typo in your copy/paste of the code – you might have split things across lines or added spaces into some of the files, or left out a closing bracket ( {{ chat.name } is not the same as {{ chat.name }})

      it’s also possible, but less likely, the problem you’re seeing is caused by using newer or different versions of some of the libraries, most likely a newer version of django. i’m especially suspicious of the 8080 port value, amongst other things.

    • Actually, looks like it was a typo on my side! I thought I’d fixed it already.

      Thanks for catching that (again…).

      WordPress seems to have eaten some of the code i wrote – I’ve corrected the page, so anyone else should be now working from correct code. If you run into more problems please let me know first, and after, if it looks like a code typo, check github (see https://github.com/aausch/django_twisted_chat/blob/master/chat/templates/chats/index.html for example) since github properly displays code and might get you unstuck 😉

  4. Hi
    I am trying to build a asynchronous chat app using django 1.8 and python. I am very new to django and find complex programming jargon hard to understand. So far your tutorial seems to provide the most comprehensive guide to building a chat app.As mentioned above I am using django 1.8 and therefore want to know if i can use this tutorial to help build my app and whether I have to make any drastic changes to the code provided in this tutorial. I hope you can help. Thanks in advance

    • i haven’t looked at the differences between versions of django in a while. this tutorial can tell you what you need to do, and give you examples of how to do it, but odds are good whole subsections will have to be rewritten to work correctly with django 1.8

      if i ever have free time to, i might do the port to 1.8 myself. but i doubt that will happen.

  5. Hello,
    I’d like to create a chat between a user and an artificial intelligence . A little like an assistance chat . Is it possible by following your plan ?
    Thanks

    • that’s an audacious plan! this rough tutorial can get you started – but it’s outdated. probably you’ll want to update it to use more recent versions of the libraries (and possibly something other than twisted?)

  6. Hello ferretfarmer! I have no clue if you still manage this at all. I am having an issue in the chat\url.py code. In the line “url(r’^(\?P\d+)/$'” it is erroring saying Dangling Metacharacter Pattern Expected.

    I am a super noob when it comes to regex, and I have no idea what it is doing at all. If you are still floating around this site, thank you for your time!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s