Community Post

How To Build A Chatroom Using Pusher And Django

Ogundipe Samuel Ayo

In these modern days, a lot of people communicate daily, and this has lead to the using of chat applications and chatrooms more often. Lots of developers always seem to want to implement chatrooms using different technologies such as XMPP, Socket.IO and a whole lot of other technologies. As a developer, I have built one using Socket.IO, and I thought that was fun. Here I bring to you, the gospel of Pusher. Since I started using Pusher, I have realized that dealing with Socket.IO has been a lot more stressful as they simplify the whole process.

Now someone might be asking, what exactly is Pusher?

Pusher is a library for making real-time applications and interactions. From the official site of Pusher, they pride themselves thus:

we spend our time maintaining reliable and scalable real-time infrastructure so you can spend yours building awesome real-time features

Django, on the other hand, is a Python web framework for building web applications. In fact, they pride themselves as

The web framework for perfectionists with deadlines

In this tutorial, we will be learning how to implement a chatroom using Django and Pusher.

What We will Build

We will be building a chatroom that allows different people to chat with each other.

This tutorial, however, hopes that you understand:

1.) The basics of Django.

2.) How to set up a Django application.

Getting Started With Pusher

To build this application, we will need to register for a Pusher account, so we can get our application id, application key, and secret.

Let's head over to Pusher and create a new account.

When you're done creating your account, on your dashboard, scroll to the bottom of the page, you will see a default app created for you, next to it is a create new app box, for now, we will just use the default app created for us by Pusher.

Let's click the default app, scroll down and copy our app id, key and secret. Once that is done, let's move to the next step.

Setting Up the Environment.

If we do not have Django installed on our system, the first step will be to install Django using the following command.

pip install django

After installing Django, we need to create our first Django application. To do that, we do:

Django-admin startproject pushers
//change directory into the pusher app
cd pushers

Now we have created a project called pushers. The next step will be to create an app. To do that, we run:

Django-admin startapp pusherchat

Note: Do not use the name pusher as the name of your app while using the startproject command, else there will be a module conflict within your app and the pusher library

The above-stated note is the reason I called the project pushers as opposed to pusher.

At this point, we can start our server using the below command:

python manage.py runserver

At this point, we can navigate to http://localhost:8000, we will be served a welcome page by Django.

Now let's install the Python Pusher library. To do that, we run the following command:

pip install pusher

Configuring the application

We however at this point have set up a Django application, and also we have used the startapp command to create an app, but Django does not know about our application yet.

We will need to add the new app we created called pusherchat to our list of installed apps.

To do this, we open up our pushers\settings.py file in our text editor, and look for the line that says INSTALLED_APPS. If you have not tampered with your settings.py file, it can be found in line no 33 of our settings.py file, and update the array to contain pusherchat, as seen below.

# Application definition

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

At this point, Django now knows about our new app.

Now, lets open up our `pusherchat\views.py and add replace it with the following lines of codes:

#render library for returning views to the browser
from django.shortcuts import render
#decorator to make a function only accessible to registered users
from django.contrib.auth.decorators import login_required
#import the user library
from pusher import Pusher
#replace the xxx with your app_id, key and secret respectively
#instantate the pusher class
pusher = Pusher(app_id=u'XXXX', key=u'XXXX', secret=u'XXXX')
# Create your views here.
#login required to access this page. will redirect to admin login page.
@login_required(login_url='/admin/login/')
def chat(request):
    return render(request,"chat.html");

What the above blocks of code do are as follows:

We import the rendering shortcut for returning views to our browser, after this, we also import the login_required decorator, to protect a particular function from being accessed by non-logged in users. We then import the Pusher library and instantiate the class with our app key, secret and app id.

We also decorate our chat function, with the login_required decorator, so it will only be accessible to logged in users. We also pass along our login_url to our decorator, which is the URL for the default admin login page provided by Django.

We define a function called chat, and pass the request object into it. We then return our render function, with two parameters, which are:

1.) The current request context,

2.) The HTML page we want to serve.

At this point, we need to add the current function we just defined to our pushers\urls.py file, as Django doesn't recognize it as a function to any route yet.

So we open up our pushers\urls.py file and replace it with the following:

"""pushers URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:

https://docs.djangoproject.com/en/1.10/topics/http/urls/

Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from django.contrib.auth import views as auth_views
from pusherchat import views

urlpatterns = [
    url(r'^$', views.chat),
    url(r'^admin/', admin.site.urls),
]

The code above is the default urls.py file except that we have added two lines of codes to the file, which are:

# we imported the views file from pusherchat
from pusherchat import views
# we added a route for the home page, which is served by the chat function in the views file.
url(r'^$', views.chat),

The above-added code simply imports the views file from the pusherchat module and then use the chat function in the module to serve the home page.

At this point, if we run python manage.py runserver on our terminal, we get a warning of unapplied migrations.

We can apply those migrations by doing the following:

python manage.py migrate

At this point, if we run our server and try to access the homepage, we get redirected to log in as seen below: However, we cannot login now, as we have not created a user yet, so we will create a superuser.

To create a user, let's run the following command in our terminal, and follow the instructions.

python manage.py create superuser

After this, if we try to sign in to our application, we will get the following error: This is because we have not created the template we served in our chat function.

By default, Django looks for template files in a folder called templates. So we create a folder called templates in our pusherchat folder and create a file called chat.html inside of the folder.

Now lets open up our chat.html file and paste in the following content:

<html>
    <head>
        <title>
        </title>
    </head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src="//js.pusher.com/4.0/pusher.min.js"></script>
    <style>
        .chat
{
    list-style: none;
    margin: 0;
    padding: 0;
}

.chat li
{
    margin-bottom: 10px;
    padding-bottom: 5px;
    border-bottom: 1px dotted #B3A9A9;
}

.chat li.left .chat-body
{
    margin-left: 60px;
}

.chat li.right .chat-body
{
    margin-right: 60px;
}

.chat li .chat-body p
{
    margin: 0;
    color: #777777;
}

.panel .slidedown .glyphicon, .chat .glyphicon
{
    margin-right: 5px;
}

.panel-body
{
    overflow-y: scroll;
    height: 250px;
}

::-webkit-scrollbar-track
{
    -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
    background-color: #F5F5F5;
}

::-webkit-scrollbar
{
    width: 12px;
    background-color: #F5F5F5;
}

::-webkit-scrollbar-thumb
{
    -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
    background-color: #555;
}

    </style>
    <body>
        <div class="container">
    <div class="row">
        <div class="col-md-12">
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <span class="glyphicon glyphicon-comment"></span> Chat
                </div>
                <div class="panel-body">
            <!-- ul element holding chat messages -->
                    <ul class="chat" id="chat">
                    </ul>
                </div>
                <div class="panel-footer">
                    <div class="input-group">
        <!-- text imput fot the messages to be typed into -->
                        <input id="btn-input" class="form-control input-sm" placeholder="Type your message here..." type="text">
                        <span class="input-group-btn">
        <!--- send button for the chat box -->
                            <button class="btn btn-warning btn-sm" id="btn-chat">
                                Send</button>
                        </span>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

In the above code, we had linked Bootstrap as our css framework, we have also linked the Pusher JavaScript library.

Additionally, we included the jQuery library, as we will use it to send POST requests to the server which now handles the Pusher broadcast.

We have also written some defined stylesheet for the chatbox.

We also have defined a ul element that will hold all our conversations.

Also, we have a text input with the id of btn-input which will represent where we type in our chat messages.

Lastly, we have a button element with an id of btn-chat which is the button which sends our chat messages.

If we save and reload our page, we should now be seeing the following screen:

Using Pusher On the Client Side.

At this point, our application is ready to use Pusher to start sending messages. To do this, let's open up our templates\chat.html page, scroll to the end of the page and paste the following script onto your editor:

<script>
    //initiate puhser with your application key
    var pusher = new Pusher('XXX_APP_KEY');
    //subscribe to the channel you want to listen to
    var my_channel = pusher.subscribe('a_channel');
    //wait for an event to be triggered in that channel
    my_channel.bind("an_event", function (data) {
        // declare a variable new_message to hold the new chat messages
        var new_message = `<li class="left clearfix"><span class="chat-img pull-left">
                            <img src="http://placehold.it/50/55C1E7/fff&text=`+data.name+`" alt="User Avatar" class="img-circle">
                        </span>
                            <div class="chat-body clearfix">
                                <div class="header">
                                    <strong class="primary-font">`+data.name+`</strong> <small class="pull-right text-muted">
                                </div>
                                <p>
                                   `+data.message+`
                                </p>
                            </div>
                        </li>`;
     //append the new message to the ul holding the chat messages
    $('#chat').append(new_message);
    });
    //wait until the DOM is fully ready
    $(document).ready(function(){
    //add event listener to the chat button click 
        $("#btn-chat").click(function(){
    //get the currently typed message
             var message = $('#btn-input').val();

            $.post({
                url: '/ajax/chat/',
                data: {
                'message': message
                },
                success: function (data) {

                    $('#btn-input').val('');

                }
            });

        })
    })

</script>

In the above block of code, we create a script tag, that will hold all our JavaScript code for the chat application. We start the JavaScript by defining a variable called pusher, and we set it to an instantiated Pusher object, passing in our application key to it.

In the next line, we subscribe to an event from our channel called a_channel, setting the output of the channel to a variable called my_channel. We then listen for a particular event by binding to that event. In this case, the event we are binding to is called an_event, which passes in data. In the context of this chatroom, our data only holds two keys which we emitted to Pusher. We will take a look at the emission process soon. we then render the data user and message into a template of li element and then append to our ul element for the chat.

In the next block of code, we handle the send button event. We start by using jQuery's document.ready() function to verify that the DOM has fully loaded, then we add a click listener to the send button with an id of btn_chat.

In the Event listener, we set a variable message to the value of the text box, and then we send a POST request to the server. Once the request is successful, we clear the value of the input.

Note: The server URL we sent the AJAX POST to currently does not exist. We will in the next step, create the URL and function to handle the URL on the server side.

Using Pusher On the Server Side.

At this point, all is set up and ready to be used at the client side, on the server side, all we have done is to instantiate the Pusher instance, but we are yet to trigger it to emit any data to the Pusher server.

We will need to do two things before we continue, which are:

  1. Import csrf_exempt so we can exempt our /ajax/chat/ URL from CSRF checks so we don't have to worry about CSRF tokens and their expiry time.
  2. Decorate our function with the crsf_exempt decorator.

Let us move into or pusherchat\views.py file, and add the following import to the top of the file.

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

We import the httpResponse library, which allows us to send raw data such as strings, JSON, or integer to the browser without returning a view file.

We also import the csrf_decorator we had talked about earlier on. We will see its use case soon.

The next step will be to create the function that handles the emitting, so after our chat function, let us add the following function:

@csrf_exempt
def broadcast(request):
    pusher.trigger(u'a_channel', u'an_event', {u'name': request.user.username, u'message': request.POST['message']})
    return HttpResponse("done");

In the above piece of code, we use the csrf_exempt decorator to exempt our broadcast function from CSRF checks. We then define the broadcast function that does the broadcasting to Pusher.

In the next line, on a one line code, we use the pusher.trigger() function which accepts three major parameters, which are:

  1. The channel we are broadcasting to.
  2. The event we are broadcasting to.
  3. An object of the data we are broadcasting.

In our own case, we are broadcasting to the channel called a_channel and the event called my_event. Are those names familiar? Of course they are. They are the channel and event we subscribed to at the client side.

Also, we pass along an object with two keys, which are the name and the message. Remember them too? Yes. They are the data we rendered in our view.

We then return an HTTP response, so the AJAX request can know the request has been successfully completed.

At this point, we have one last thing to do, and our application is ready to be used.

The last step will be to configure our urls.py, so it knows which function handles the /ajax/chat/ route. Let's head into our pushers\urls.py file and add one more route to our route block. We add the following:

url(r'^ajax/chat/$', views.broadcast),

If we save and reload, if all went well, our chatroom should be working well, like what we have below.

Conclusion.

If you have followed this tutorial up to this point, you will have learned how to implement a basic chatroom using Pusher and Django.

Although you could improve on a lot of things such as using CSRF tokens with your AJAX request, the use of csrf_exempt was intentional, so as to give you another method of dealing with AJAX requests.

We have also learned how to force a particular section of our application for only registered users.

We have seen how easy it is to use Pusher. Go ahead, build amazing apps, and preach the gospel of Pusher.

To try out the demo, you can go to https://rocky-inlet-95261.herokuapp.com/, and login with the following details:

  1. username: samuel, password: olufemi1
  2. username:ayo, password: olufemi1

Ogundipe Samuel Ayo

7 posts

Self Taught Software Developer. Software Developer At Crust Resources Conversant with Php (Codeigniter and Laravel), Python (Flask and Django), C# (WPF), Javacript (Vue, Angular, React-native, Node.js (Adonis.js, Express) ). Can Use the Mean Stack, But never used it For anything Serious