Realtime Django Using Node.js and Socket.IO (Posted on January 12th, 2013)

Our goal for today is to build a realtime chatroom using Django, Redis, and Socket.IO. While we'll be building a chatroom the concepts can be applied to almost any web app. At a high level this post will show you how you can convert your REST based app into a realtime web app. I'll be using Django to create the REST based portion but feel free to use any language/framework you're comfortable with. With that said let's jump into the code and get setup with what we need.

The Setup

  • Django 1.4+
  • Redis 2.6.x (somewhat optional, but recommended)
  • Redis-py 2.7.x (only needed if you're using Redis)
  • Node.js v0.8.x
  • Socket.IO v0.9.x
  • Cookie v0.0.5
  • Some sort of database or sqlite if you consider that a database

Your mileage may vary with other versions. I just haven't tested the code with other versions. As of writing these are the latest releases. If you have none of these technologies installed here is a quick guide I've compiled from each package's repo for Ubuntu. You can follow the commented links to learn about other operating systems.

#https://docs.djangoproject.com/en/dev/topics/install/
sudo apt-get install python-pip
sudo pip install django

#http://redis.io/download
sudo apt-get install redis-server

#https://github.com/andymccurdy/redis-py
sudo pip install redis    
    
#https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs

#https://github.com/LearnBoost/socket.io
npm install socket.io

#https://github.com/shtylman/node-cookie
npm install cookie

Django Project

Let's get started with this bad boy!

django-admin.py startproject realtime_tutorial && cd realtime_tutorial
python manage.py startapp core
mkdir nodejs

Now that our file structure is all setup lets update the settings file to include our database information. If you haven't created a blank database for this project yet go ahead and create one now. Here is a copy of my settings file for reference. I've added "core" to my installed apps and also told Django where it can find my templates and login urls. If you have a certain way you like to setup your settings feel free to do so but make sure to add the proper installed apps.

The Model

The models for this project is going to be really simple. We have a comment which contains some text and is associated with a user. If you want to make it more complex you could also add a chatroom variable. To keep things simple we'll just stick with two fields.

from django.db import models
from django.contrib.auth.models import User

class Comments(models.Model):
    user = models.ForeignKey(User)
    text = models.CharField(max_length=255)

Since this is the only model we will be using it is safe to run a syncdb and create the tables for our app. Feel free to create a few users on this step to do some testing with later on.

python manage.py syncdb
python manage.py createsuperuser

Node Server With Socket.IO

This is the part where our realtime message sending and receiving will occur. We'll use Node.js to create an app server but will then rely on Socket.IO and Redis to do the grunt work. In the nodejs directory create a file called "chat.js" and place this in there:

var http = require('http');
var server = http.createServer().listen(4000);
var io = require('socket.io').listen(server);
var cookie_reader = require('cookie');
var querystring = require('querystring');

var redis = require('socket.io/node_modules/redis');
var sub = redis.createClient();

//Subscribe to the Redis chat channel
sub.subscribe('chat');

//Configure socket.io to store cookie set by Django
io.configure(function(){
    io.set('authorization', function(data, accept){
        if(data.headers.cookie){
            data.cookie = cookie_reader.parse(data.headers.cookie);
            return accept(null, true);
        }
        return accept('error', false);
    });
    io.set('log level', 1);
});

io.sockets.on('connection', function (socket) {
    
    //Grab message from Redis and send to client
    sub.on('message', function(channel, message){
        socket.send(message);
    });
    
    //Client is sending message through socket.io
    socket.on('send_message', function (message) {
        values = querystring.stringify({
            comment: message,
            sessionid: socket.handshake.cookie['sessionid'],
        });
        
        var options = {
            host: 'localhost',
            port: 3000,
            path: '/node_api',
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': values.length
            }
        };
        
        //Send message to Django server
        var req = http.get(options, function(res){
            res.setEncoding('utf8');
            
            //Print out error message
            res.on('data', function(message){
                if(message != 'Everything worked :)'){
                    console.log('Message: ' + message);
                }
            });
        });
        
        req.write(values);
        req.end();
    });
});

Up top we do our imports and create an http server to listen on localhost port 4000. We then subscribe to the Redis "chat" channel. We could easily call this "rabblerabble" as long as we do the same on the publish end in our Django view.

Next we setup Socket.IO to be able to use the cookie that Django sets for the localhost domain. This enables us to access the cookie data via socket.handshake.cookie['the_key_we_want']. This is how we will get the user's sessionid.

After we setup the cookies with Socket.IO we can then handle some events. The first event is for our Redis pubsub channel. When our subscriber notices a new message has been posted it will send the message to all clients on the site.

The other event is when the client sends a message through Socket.IO. We use the querystring module to create a query that can be sent to our Django server. Our Django server will be running on localhost port 3000 but you can change that as needed. The path is set to /node_api which is a URL we will create on the Django side later on. Once we send the querystring we wait for Django to save the comment and send us back "Everything worked :)". If we don't get that back then we output the error to the Node console.

A note about not using Redis

You don't really need to use Redis for this project at all. I found it to be a good learning experience. If you want to bypass Redis you can create a route, using Express or some other library, in the above code that receives a message from Django when a comment has been saved. Then you can broadcast the comment to all clients via Socket.IO.

The Template

This is where all our HTML and client side javascript will be placed. This will allow us to display comments and interact with our Node server.

<!DOCTYPE html>
<html>
<head>
  <title>Realtime Django</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js" type="text/javascript"></script>
  <script src="http://localhost:4000/socket.io/socket.io.js"></script>
  <script>
    $(document).ready(function(){
      var socket = io.connect('localhost', {port: 4000});
      
      socket.on('connect', function(){
        console.log("connect");
      });
      
      var entry_el = $('#comment');
               
      socket.on('message', function(message) {
        //Escape HTML characters
        var data = message.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
        
        //Append message to the bottom of the list
        $('#comments').append('<li>' + data + '</li>');
        window.scrollBy(0, 10000000000);
        entry_el.focus();
      });
                     
      entry_el.keypress(function(event){
        //When enter is pressed send input value to node server
        if(event.keyCode != 13) return;
        var msg = entry_el.attr('value');
        if(msg){
           socket.emit('send_message', msg, function(data){
                console.log(data);
           });
        
        //Clear input value   
        entry_el.attr('value', '');
       }
      });
    });
  </script>
</head>
<body>
    <ul id="comments">
        {% for comment in comments %}
            <li>{{comment.user}}: {{comment.text}}</li>
        {% endfor %}
    </ul>
    <input type="text" id="comment" name="comment" />
</body>
</html>

Up top we're connecting to our node server with socket.io on localhost port 4000. When we get a message from the server we do some escaping on the content and then append it to our comments list. When we want to send a message we check for a keypress of 13 (enter key) on our input box. Once that is pressed we emit the message to the server to be handled. Once it is saved to our database by Django we'll get a "message" event which will append it to our chat list.

Our Django view that we create in the next step will just be loading a "comments" variable. So we set that up and loop through them all at the bottom. This part is only used when the page is first loaded. Our javascript will append data to this list as new data comes in from our Node server.

The View

Go ahead and open up realtime_tutorial/core/views.py and edit it to look like mine:

from core.models import Comments, User

from django.shortcuts import render
from django.http import HttpResponse, HttpResponseServerError
from django.views.decorators.csrf import csrf_exempt
from django.contrib.sessions.models import Session
from django.contrib.auth.decorators import login_required

import redis

@login_required
def home(request):
    comments = Comments.objects.select_related().all()[0:100]
    return render(request, 'index.html', locals())

@csrf_exempt
def node_api(request):
    try:
        #Get User from sessionid
        session = Session.objects.get(session_key=request.POST.get('sessionid'))
        user_id = session.get_decoded().get('_auth_user_id')
        user = User.objects.get(id=user_id)

        #Create comment
        Comments.objects.create(user=user, text=request.POST.get('comment'))
        
        #Once comment has been created post it to the chat channel
        r = redis.StrictRedis(host='localhost', port=6379, db=0)
        r.publish('chat', user.username + ': ' + request.POST.get('comment'))
        
        return HttpResponse("Everything worked :)")
    except Exception, e:
        return HttpResponseServerError(str(e))

Let's breakdown what's going on here. Our "home" view is pretty standard. I'm using select_related to also grab the username for each comment rather than doing a query for each comment individually when the page is first loaded.

The second view is what our Node app is sending data to. We grab the sessionid from the POST data and decode it to grab the user id associated with it. Once we have the user and can verify that they exist we can create the comment. Now we send the username and comment to our Redis server. Since our pubsub channel name is "chat", we send our data to that channel.

The URLs

The URLs are pretty straight forward. For logging in and out we'll use the default Django views. We'll also use the default admin panel login template.

from django.conf.urls import patterns, include, url

urlpatterns = patterns('',
    url(r'^$', 'core.views.home', name='home'),
    url(r'^node_api$', 'core.views.node_api', name='node_api'),
    url(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'admin/login.html'}, name='login'),
    url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}, name='logout'),
)

Start It Up!

That should be all we need to get this working. Let's start both of the servers.

python manage.py runserver localhost:3000

#In a new terminal tab cd into the nodejs directory we created earlier
node chat.js

I've posted the source code to github incase you want to check it out and play around with the code. If you're looking for a quick challenge go ahead and modify the code to allow users to create/join chatrooms. You could also drop the Django portion and implement a different backend such as PHP or Rails.

As always if you have any feedback or questions feel free to drop them in the comments below or contact me privately on my contact page.

Tags: Django, Redis, Node.js

Comments:

  • Andreas - 1 year, 10 months ago

    I think Node.js isn't the best choice in this regard. What I found to be a better solution is tornado + sock.js. Socket.io is ok, but it is very stringent on the protocol it uses. Tornado can be run from a management command in Django, it can even use the ORM (sparingly, otherwise the thread blocks...). However you need two redis packages, redis-py and tornado-redis, one is blocking, one is not. Don't get me wrong, I like javascript (especially coffeescript). I just don't see a good reason to use it server side. Granted, node with coffeescript is a tad more elegant than tornado, but then there's Dieselweb and Gevent, which use a more sequential coding style than what tornado does with the callbacks or generator syntax.

    reply

  • muhia - 1 year, 10 months ago

    Hi. i usually don't comment a lot, but i just had to do it this once. i was wondering if you are really a human or a bot, coz i swear i saw this exact comment on another site a few days ago. Anyway, i think it is relevant tho, thanks

    reply

  • Line height ftw - 1 year, 10 months ago

    please try document.body.style.lineHeight = '1.5em' within your browser console, isn't that better?

    reply

  • Devastator - 1 year, 10 months ago

    Hey man, thanx for this, very good job! Since there is a ... api to socket.io, you must use only examples like yours. Bye.

    reply

  • FZambia - 1 year, 10 months ago

    Hello! Thanks for the article. Recently i also tried to implement something like this, but when event(chat message) happened - I first saved it in Django - this allows to easily handle different user permissions. And then I send POST request to asynchronous server. So this is a "one-direction" realtime. I've implemented this using Tornado, Node, Twisted as async servers and Socket.IO and Sock.js as client-side libs. Here is repo with all of this: https://github.com/FZambia/django-realtime-playground

    reply

  • Max Burstein - 1 year, 10 months ago

    Just took a look through your projects in that folder. Very cool stuff. It sounds like you're indeed doing something similar to my code except cutting out Redis. Out of curiosity have you run benchmarks to compare the speed/reliability of the different backends?

    reply

  • FZambia - 1 year, 10 months ago

    Yes, similiar, but not only except Redis - new data first go to Django and then to Node or Tornado. Of course I thought about making benchmark, but have no time enough at the moment.

    reply

  • Peter Bengtsson - 1 year, 10 months ago

    I too prefer Python That's why I wrote: https://github.com/peterbe/django-sockjs-tornado

    reply

  • Dami O - 1 year, 10 months ago

    If you are hitting 500 on Django's server, you may want modify chat.js there is a pull request here https://github.com/damilare/django-realtime-tutorial/commit/3496f699ea4c8e501600f4be8c8057ca0ba3878e

    reply

  • Max Burstein - 1 year, 10 months ago

    Thank you for the pull request. I have merged it into my codebase. I think you're getting the 500 error due to something else though. The original code works for me on my dev environment. The code works since we're setting the type as POST which is basically overriding the shortcut .get method in the node http module. Nonetheless, .request is the right method to be calling as you pointed out.

    reply

  • Oz Katz - 1 year, 9 months ago

    I have actually tried to take this exact approach (and stack) and turn into into something more generic that is reusable (not only for Django but possibly for other frameworks as well). it's called Announce.js - https://github.com/ozkatz/announce.js The django client is here: https://github.com/ozkatz/django-announce/

    reply

  • miguel - 1 year, 9 months ago

    This implementation reminds me of what Armin Ronacher posted here: http://lucumr.pocoo.org/2012/8/5/stateless-and-proud/ It's similar but not quite how he describes it I think. Either way, thanks a lot, that is a good boilerplate code.

    reply

  • Johniek - 1 year, 9 months ago

    Hi, first thanks for this example, secondly i use u code and in views.py you getting POST['comment'] - why? In me POST does not sended on view, ... I mean this line: socket.emit('send_message', msg, function(data){})

    reply

  • Max Burstein - 1 year, 8 months ago

    Once the page is loaded the client gets all the data from the node server. The messages are also sent off to the Django server so that they can be stored. This way when a new client connects they can see the chat history. socket.emit sends the data to all clients except the one that sent the message.

    reply

  • Rohit - 1 year, 8 months ago

    Hi just downloaded code from github installed dependencies but I am getting an error events.js:71 throw arguments[1]; // Unhandled 'error' event ^ Error: connect ECONNREFUSED at errnoException (net.js:770:11) at Object.afterConnect [as oncomplete] (net.js:761:19) When i am trying to enter some value in the text box please help me out .

    reply

  • Max Burstein - 1 year, 8 months ago

    Is the request ever getting to the Django server? Also what version of Django/Node are you using?

    reply

  • Rohit - 1 year, 8 months ago

    I am using django.VERSION (1, 4, 0, 'final', 0) And my node version is v0.8.18 Yes when i am log in i am getting an input box ,But whenever I i am hitting enter button after entering some value in input box i am getting this error.

    reply

  • Max Burstein - 1 year, 8 months ago

    Alright shoot me an e-mail through my contact form and I'll be able to help you out better. I feel like the issue is coming from the request not getting to the django server. In the console for the Django server, is the request to /node_api reporting a 200 status code?

    reply

  • n3rV3 - 1 year, 8 months ago

    Just tried it out, this is really awesome.. but would it be possible to have a similar setup only of Python based components? Please suggest if there are any good alternatives to node.js in Python.

    reply

  • Max Burstein - 1 year, 8 months ago

    You could utilize https://github.com/abourget/gevent-socketio or one of the other libraries that port socket.io over. I've found that the gevent one is relatively well supported.

    reply

  • dougvk - 1 year, 7 months ago

    Max - thanks for writing this up. I was wracking my brain for how to use django's authentication kickassedness but ditch the request/response. Are there security vulnerabilities in ditching the CSRF token?

    reply

  • Max Burstein - 1 year, 7 months ago

    You should be able to include the Django CSRF value from the cookie that it sets. When I tried it, it wasn't working so I ditched the CSRF token. One thing that CSRF tokens prevent is people from making phishing sites and then sending the post data to your server. You can read more about them here http://en.wikipedia.org/wiki/Cross-site_request_forgery

    reply

  • Stacy - 1 year, 7 months ago

    Hi, I'm getting an ImportError: No module named debug_toolbar.... Why is that?

    reply

  • Max Burstein - 1 year, 7 months ago

    You can install the debug toolbar which is super useful for debugging Django apps or just remove the app from the middleware and installed apps file in your settings.py. Here is where you can get it from https://github.com/django-debug-toolbar/django-debug-toolbar

    reply

  • Venkat - 1 year, 7 months ago

    Why do you get the user-id from session and not from request.user?

    reply

  • Marcel Chastain - 1 year, 3 months ago

    Pretty sure it's because the node.js process is posting to the django view, not the user's logged in browser.

    User posts message -> socket.io server (via node.js) -> django

    Normally the session_id would be included in the cookie that comes along with the user's browser's request, and it would go straight to django, who knows how to handle cookies easily.

    In this system the request goes to node.js, then node.js re-posts that to django (with the session_id in the request POST), and django does a manual lookup of the session. No need for cookies.

    reply

  • Sara - 1 year, 7 months ago

    Why do you use debug_toolbar?

    reply

  • Max Burstein - 1 year, 7 months ago

    It's a super useful tool that I include in all of my projects by default. It allows me to see slow running queries and such for making optimizations. Definitely check it out if you've never used it.

    reply

  • Jenna - 1 year, 7 months ago

    I'm getting this error when I try your code: "DatabaseError: no such table: django_site"

    reply

  • Jenna - 1 year, 7 months ago

    This is at the /login/ portion. DatabaseError at /login/ no such table: django_site Request Method: GET Request URL: http://localhost:3000/login/?next=/ Django Version: 1.5.1 Exception Type: DatabaseError Exception Value: no such table: django_site Exception Location: /usr/local/lib/python2.7/dist-packages/django/db/backends/sqlite3/base.py in execute, line 362 Python Executable: /usr/bin/python Python Version: 2.7.3 Python Path: ['/home/euridice/realtime_tutorial', '/usr/local/lib/python2.7/dist-packages/pip-1.3.1-py2.7.egg', '/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/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: Wed, 24 Apr 2013 17:12:34 -0400

    reply

  • Max Burstein - 1 year, 7 months ago

    I think you need to setup the database information in your settings.py then run "python manage.py syncdb". That should create the table for you.

    reply

  • Sara - 1 year, 6 months ago

    "If you're looking for a quick challenge go ahead and modify the code to allow users to create/join chatrooms." Could you provide a quick tutorial on how to do this? Would I just be using Socket.io's documentation for implementation?

    reply

  • Max Burstein - 1 year, 6 months ago

    This would be a good place to get started with https://github.com/LearnBoost/socket.io/wiki/Rooms You'll also want to add an additional column in your models for storing the data. This way you can keep a history of each channel. Then you'll also want to add the chatroom name/id to the data sent over redis or just give each chatroom it's own redis pubsub channel. If you some code that's not working or something feel free to shoot me an e-mail through my contact form and I'd be happy to take a look at it.

    reply

  • trojjer - 1 year, 6 months ago

    That was interesting. You could've used a FormView with a custom queryset for node_api. I love Class Based Views in Django, having been introduced to them with Django itself at work and having no experience of working with the functional views. With a FormView you could just specify a ModelForm for the Chat model. You could then pass in the User ref to the form in a custom get_form_kwargs() method, and validate it separately in the ModelForm's clean_data(). You could also declare a session_id field explicitly and have the ModelForm put it in cleaned_data['session_id']. I'm going on a bit, but I saw that you left the code open to a basic SQL injection vulnerability by not sanitising user input POST data :P

    reply

  • trojjer - 1 year, 6 months ago

    A followup for anyone puzzled by CBVs in Django. I still find myself struggling with the Django docs sometimes; every Django dev using CBVs should know about this awesome third party API listing: http://ccbv.co.uk/projects/Django/1.4/

    reply

  • Max Burstein - 1 year, 6 months ago

    Django's ORM will actually sanitize the data for us. No need for us to do any extra work on our end.

    reply

  • Anonymous - 1 year, 4 months ago

    after doing as instructed in the tutorial. i am not able to get the desired output. on typing localhost in the browser ,i get "It works!

    This is the default web page for this server.

    The web server software is running but no content has been added, yet." and typing localhost:3000 gives an error. what to do?

    reply

  • Max Burstein - 1 year, 4 months ago

    Can you try checking out the repo from github and seeing if that works for you. This sounds like an error on the Django side. If the default server page is coming up that means the urls.py is redirecting to the core.views file. Though usually you need to go to the port to use Djano's runserver. So if you're seeing data by just going to localhost that's probably apache or some other web server running in the background. Make sure you have Django setup properly.

    reply

  • karthik - 1 year, 4 months ago

    Not working in Django 1.5. And I didn't change anything in the original code.

    Error: GET http://localhost:4000/socket.io/1/?t=1373456321029 500 (Internal Server Error)

    reply

  • Max Burstein - 1 year, 4 months ago

    Were you able to get it working with Django 1.4? I don't think any changes happened in 1.5 that would prevent this from working. The try catch should catch any errors if it's making it to the view. If it's not making to the view then something else is up. You can try setting your app to debug = False. Then setup a simple mailserver like such:

    //This goes in your django settings file

    EMAIL_HOST = 'localhost'

    EMAIL_PORT = 1025

    //Run this from a command prompt

    python -m smtpd -n -c DebuggingServer localhost:1025

    Make sure you have an e-mail set for the admin variable in your settings file. Then you should be able to look at the errors through the console you're running your server off of. This should print out the error and give you a better idea of how to fix it.

    I can look into this more over the weekend if needed. Let me know how it works out.

    reply

  • Conor - 1 year, 3 months ago

    Hello Max this tutorial has been great. I got everything working fine on localhost but I haven't been able to get one thing working in production. The cookies are no longer in the header of the request sent to the node server. They are set just the same on the browser and I allowed them to be used for all connections just as localhost. But there's nothing in the header. Any ideas?

    reply

  • Conor - 1 year, 3 months ago

    I got it working. I specified the URL client side as the IP address the browser declared that cross domain and wouldn't pass cookies.

    Also, Max, I believe I found a bug in your nodejs.

    You wrote io.sockets.on('connection', function (socket) {

    //Grab message from Redis and send to client
    sub.on('message', function(channel, message){
        socket.send(message);
    });
    ...
    });
    

    This means each time a client connects, there's going to be another listener added to the sub. If 100 clients connect through the day, you'll have 100 listeners on the sub. The listeners aren't removed on disconnect. I solved it by simply moving it outside of the connection listener.

    Thanks for the tutorial, it helped a lot.

    reply

  • Max Burstein - 1 year, 3 months ago

    Glad you found the tutorial helpful. I'll have to look into the listener thing. What you're saying sounds right though. I know that the way it's setup it will reuse the redis connection rather than making a new one for each client which is nice.

    reply

  • Loni - 1 year, 2 months ago

    Hi, thank you for this Post!

    I am kinda confused: why is the 'node_api' request made by the node server? I mean, we could have made this request in the index.html script; this way the node server will only be in charge of sending notifications.

    Thanks you :)

    reply

  • Max Burstein - 1 year, 2 months ago

    That solution would definitely work as well. You could easily make an ajax request to the Django server.

    reply

  • Christian - 1 year, 2 months ago

    Hi, bro! Great tutorial! But I got the next problem: When I run the application and try to send a message it doesnt do anything and the socket.io server chrashes showing me:

    events.js:72 throw er; // Unhandled 'error' event ^ Error: connect ECONNREFUSED at errnoException (net.js:901:11) at Object.afterConnect [as oncomplete] (net.js:892:19) christian@christian-R480-R431-R481:~/Documentos/Github/django-realtime-tutoria

    What could be happening? Ang again, great tutorial! =D

    reply

  • Max Burstein - 1 year, 1 month ago

    Not 100% sure on the answer to this one. I think the issue is that your django server isn't running on localhost:3000. If you want to run it at a different location be sure to update the node server to make requests to that URL. If your Django server is running on localhost:3000 and you still have the same issue let me know and I'll try and dig further into the issue.

    reply

  • mira - 10 months, 1 week ago

    Hi, this is a great tutorial, thanks for posting! Unfortunately I'm having similar problem. Is there any solution? and django server is running in my case.
    $ node chat.js info - socket.io started

    events.js:72 throw er; // Unhandled 'error' event ^ Error: Redis connection to 127.0.0.1:6379 failed - connect ECONNREFUSED at RedisClient.on_error (/Users/mira/Documents/Projects/node_modules/socket.io/node_modules/redis/index.js:149:24) at Socket.<anonymous> (/Users/mira/Documents/Projects/node_modules/socket.io/node_modules/redis/index.js:83:14) at Socket.EventEmitter.emit (events.js:95:17) at net.js:441:14 at process._tickCallback (node.js:415:13)

    reply

  • Max Burstein - 10 months ago

    I think this is because your Redis server isn't running. If you're on Ubuntu or some other Unix based client type "redis-cli" and see if you connect to the server.

    reply

  • Abud - 1 year ago

    Hello, I installed the app as a module to my main Django 1.5.4 site on development and made necessary adjustments to urls.py etc. However when I visit the chat page, I just see a field and nothing else. Also I get warn - handshake error error from node.js. I'm new to node.js stuff so appreciate your hints.

    reply

  • Max Burstein - 1 year ago

    You won't see anything but a field until comments start showing up. If you already have comments in your database and they aren't showing up then the problem is in your views somewhere.

    As for the handshake error I'm not really sure what advice to give without a more detailed stacktrace if you can. My guess is that it has something to do with "sessionid: socket.handshake.cookie['sessionid']" (line 36). Use your browsers debugger console to inspect the cookie you have set for the site. Make sure it has a sessionid. If it doesn't or you don't have a cookie then Django may be at fault here. If you have the cookie and the sessionid then I'm not really sure what the error is.

    If the node code is working and all you're getting is a warning it may be because the code for this tutorial is outdated. If you're interested in an all python approach checkout https://github.com/mburst/gevent-socketio-starterkit

    Feel free to shoot me an e-mail if you have any questions.

    reply

  • Jelly - 11 months, 4 weeks ago

    Cool job! Really! But I have a question, what if the cookie if forbidden by users? since it seems that you depend on cookie to identify users.

    reply

  • sanny jane yacapin - 11 months, 3 weeks ago

    Thank you for your post its a little confusing but ill figure it out. I think your topic is a bit related to my blog.i hope it would help in the future.

    reply

  • riel - 11 months, 2 weeks ago

    Hello, I got it working in my local but I don't know how to set it up in my production environment, do you have any idea on how to deploy it? I'm using uwsgi and django in my production. Thank you so much.

    reply

  • Max Burstein - 11 months, 1 week ago

    You'll need to set the socket.io link to point to the right server. Right now we have it setup for localhost. So you'll need to point it to example.com or whatever your domain is. The Django stuff is all standard so if you're having issues with that then you'll want to look in to your settings.py file for issues. What issues are you having with production? Also feel free to shoot me a message through my contact form and I'll be able to help you quicker via e-mail.

    reply

  • Julius Bernhard - 11 months, 1 week ago

    Very nice post..

    reply

  • Aenima - 10 months, 3 weeks ago

    Great Job!! I have one question. Do you have any idea why I'm not sending the cookie in my request?? I'm already logged in: { headers: { host: 'localhost:4000', connection: 'keep-alive', 'cache-control': 'max-age=0', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36', origin: 'http://127.0.0.1:8000', accept: '/', referer: 'http://127.0.0.1:8000/', 'accept-encoding': 'gzip,deflate,sdch', 'accept-language': 'es,en;q=0.8' }, address: { address: '127.0.0.1', port: 53263 }, time: 'Mon Jan 06 2014 10:20:39 GMT-0300 (ART)', query: { t: '1389014439988' }, url: '/socket.io/1/?t=1389014439988', xdomain: true, secure: undefined, issued: 1389014439992 }

    reply

  • Max Burstein - 10 months, 3 weeks ago

    The reason is because cookies are port specific, not just domain specific. So your socket.io request is going to port x while django cookie is set for port y. One way around this would be to use a reverse proxy such as HAproxy so that your django and socket.io requests go to the same port.

    reply

  • Robin - 8 months, 2 weeks ago

    I am so very grateful to you for this post! Every thing that I learnt about node.js and socket.io was from this blog. But there are still some things which I have to learn. I want to learn how to build timeline feeds and messaging system. The business model for messaging as well as timeline have been setup in Django, but I just don't know how to integrate them together. And also, do I have to create different redis database for both timeline and message system? And lastly, since the template listens to only one socket.io server, how to differentiate between two node servers? If you could guide me and help me with the above said problems, would be very thankful! Thanks again!!! :)

    reply

  • Max Burstein - 8 months, 2 weeks ago

    I'll try and take this one step at a time. Feel free to e-mail me through my contact form for extra guidance.

    One way to push content to your redis channel is to override the model's save method. Every time you create the model successfully you can post it to a Redis channel. Then a listener on the Node.js side will pick it up and send it to your client.

    You can do everything in one Redis instance. Just publish to a timeline channel and messaging channel. You could technically have a different channel for each user if you wanted. No need to fire off a create statement or anything like that. Just push to the channel and listen for that same channel name on the other end.

    You'll want to use a reverse proxy server that supports sockets (nginx or the dev version of haproxy to name a few) to differentiate between your two servers. Basically you have 1 server that listens for requests. Then that server routes that request to one of your two node servers. The client usually doesn't even know about it.

    I have a blog post in the works on how you can do all this with Python/Django (no Node.js) but I really have no clue when I'll get around to finishing it. In the meantime, if you're interested, you can checkout https://github.com/mburst/gevent-socketio-starterkit. The djangoproject folder is a blank starter project and the rest are examples.

    reply

  • sergey - 5 months, 1 week ago

    Hello, thanks for your experience. This code looks very simple and comfortable to quick start learning nodejs and socket.io. But when I try to run this code a found trouble with io.configure method. Look like new version of Socket.io doesn't have such method or something is wrong with my installation.

    reply

  • Daniel - 4 months, 4 weeks ago

    Hi. First, i want to say thank you. I tried this in my local pc and it works, but when i tried to use it in my production server, it don't work. The problem is that my cookie only contain the csrf_token but not a session_id. Any suggestion of this? Thanks for your time.

    reply

  • Daniel - 4 months, 3 weeks ago

    I've solved the problem. In my settings.py I only need to add the next line: SESSION_COOKIE_DOMAIN=".dominio.com" because i have a subdomain for serve my chat.js app and when i went from a domain to another subdomain the sessionid were lose.

    reply

  • Max Burstein - 4 months, 1 week ago

    Yup that'll do it. Glad you were able to figure it out.

    reply

  • Dra┼żen - 3 months, 3 weeks ago

    Great explanation of how to do push notifications in Django. Helped me a lot. Thanks for writing it!

    reply

  • Anonymous - 3 months ago

    How would you develop this to make an app for chatting between users (private chat)?

    reply

  • Max Burstein - 2 months, 1 week ago

    Could have socket users create a username than keep an object on the node side of username => socket. Then when you get a message for a username check that object you created and call socket.send on the socket associated with the username. Don't forget to remove users from the object on socket disconnect.

    reply

  • Anonymous - 1 week, 1 day ago

    Hi, I tried following this, but I'm getting this error and I don't know why. Could you help me out, please? Thanks!

    module.js:340 throw err; ^ Error: Cannot find module 'socket.io/node_modules/redis' at Function.Module._resolveFilename (module.js:338:15) at Function.Module._load (module.js:280:25) at Module.require (module.js:364:17) at require (module.js:380:17) at Object.<anonymous> at Module._compile (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10)

    reply

  • Anonymous - 5 days, 21 hours ago

    Nevermind, found the problem--socket.io has updated and no longer includes redis.

    reply