Build a Twitter Web application

Writing for Web 2.0 with Django and jQuery

Learn how to create a Twitter-enabled Web 2.0-style application using Django, jQuery, and the python-twitter wrapper that you can easily use and plug in to your own Django project. With this application, you'll be able to see recent tweets, post updates, and show your friends and followers.

Share:

Cesar Otero, Consultant, Freelance Consultant

Cesar OteroCesar Otero is a freelance Java and Python consultant. He holds a degree in electrical engineering with a minor in mathematics.



03 December 2009 (First published 24 November 2009)

Also available in Russian Japanese Portuguese

These days, it seems like Twitter is ubiquitous. Politicians, actors, parents — you name it — are using the social networking medium. Clients are not only demanding Web applications that are Twitter-enabled but that also have a slick Web 2.0 look to them, which is only achieved through Ajax. Enter Django and jQuery.

Figure 1. Twitter panel displaying recent tweets
Screenshot shows Twitter panel with recent tweets

Dependencies

Frequently used acronyms

  • Ajax: Asynchronous JavaScript + XML
  • API: Application programming interface
  • CSS: Cascading style sheet
  • FAQ: Frequently asked questions
  • HTML: Hypertext Markup Language
  • HTTP: Hypertext Transfer Protocol
  • REST: Representational State Transfer
  • UI: User interface

I highly recommend going through the Django tutorial before commencing work on this. You will also need a solid JavaScript foundation. It goes without saying that you will need a Twitter account and must know the lingo, such as what a tweet is. Your arsenal should contain Python, Django, jQuery, and the python-twitter wrapper. See Resources for links.

You will also need the sqlite3 database, included with Python, for containing the session data; the Twitter infrastructure will save everything else. But, as an improvement on this application, you could certainly integrate a more sophisticated database structure to keep statistics, copies of tweets, or what's on your Twitter account on your own server, for example. This article also assumes that you're working on Linux®.

I tested the sample application with Python V2.5.2. Python is installed by default on most Linux machines; if you don't have Python installed, see Resources for information on downloading and installing the language.

Twitter's data is exposed to the public through two APIs: the search API and the RESTful API. (The Twitter FAQ mentions future plans for a single API.) A REST Web service is a Web service implemented in HTTP that follows REST principals (see Resources for more information).

Installing the python-twitter wrapper

The final component you need is python-twitter, which is "a python wrapper around the Twitter API and the twitter data model," according to the project site. There are several libraries for interfacing with Twitter's services in many languages — anything from Ruby to Eiffel. Currently, there are five libraries for interfacing Python with Twitter: Python-twitter by DeWitt Clinton, python-twyt by Andrew Price, twitty-twister by Dustin Sallings, twython by Ryan McGrath, and Tweepy by Josh Roesslein.

The python-twitter library requires the dependency simplejson (see Resources). After you download simplejson, install it by issuing the commands shown in Listing 1.

Listing 1. Commands for installing simplejson
tar -zxvf simplejson-2.0.9.tar.gz
cd simplejson-2.0.9
sudo python setup.py build
sudo python setup.py install

If you prefer to use the egg package, you can use the command sudo easy_install simplejson-2.0.9-py2.5-win32.egg.

Now, you can install python-twitter. After you download the package, execute the commands shown in Listing 2.

Listing 2. Commands for installing python-twitter
tar -zxvf python-twitter-0.6.tar.gz
cd python-twitter-0.6
sudo python setup.py build
sudo python setup.py install

Installing Django

Next on the installation list is Django, a powerful Python Web framework. The sample application in this article was written with V1.1.1. The installation is just as easy as simplejson and python-twitter. After downloading the package, enter the commands shown in Listing 3 in any terminal.

Listing 3. Commands for installing Django
tar -zxvf Django-1.1.1.tar.gz
cd Django-1.1.1
sudo python setup.py build
sudo python setup.py install

Verify that Django is on your path by trying the command $ django-admin --version. If everything went as planned, you're ready to start coding.


The python-twitter library

Now that you have installed python-twitter, you can explore its capabilities. To access the library, use the command import twitter. The twitter module provides wrappers for the Twitter data model and the API. There are three data model classes: twitter.Status, twitter.User, and twitter.DirectMessage. All API calls return an object of one of these classes. To access the API, make an instance of twitter.Api() (notice that the first letter is capitalized). Creating an instance without any arguments to the constructor is valid, but you're limited to method calls that don't require a login. To have login access, use the following code:

import twitter
api = twitter.Api(username='yourUserName', password='yourPassword')

With an instantiated Api object, you can get your followers, people you are following, and statuses (tweets); post your current status; etc. For example, to get a list of a user's statuses, call statuses = api.GetUserTimeline(), which returns a list of twitter.Status objects. To create another list with the text (strings) of your tweets, you could use list comprehension: >>>print [s.text for s in statuses].

Table 1 lists classes and methods you will be using in your Web application. For the full documentation, use the pydocs —$ pydoc twitter.Api, for example — or visit the python-twitter docs page (see Resources).

Table 1. Some useful python-twitter API methods
Method nameReturnsDescription
GetUserTimeline()List of twitter.Status objectsGet all tweets for a user
GetFriends()List of twitter.User objectsGet a list of followers
PostUpdate()A single twitter.StatusPost a tweet

Adding jQuery to the mix

To be more specific, you're not just using the jQuery core but also the jQuery UI, which provides widgets, themes, and animation. Head over to the ThemeRoller page, and in the left pane, click the Gallery tab. Choose a theme you like (I used Cupertino for the sample application), then click Download below the theme name. Doing so links to the actual download page; choose the current (stable) version, which at the time of this writing was 1.7.2 for jQuery V1.3+.

There is no installation process; just extract the ThemeRoller file and place the correct directories/files in the right place. You're also welcome to create your own theme if you wish, but for the sake of brevity, I used one of the pre-rolled themes.


Creating your Django project

Next, you need to create a new Django project. Use the command django-admin startproject pytweetproj where pytweetproj is the name used for the project throughout this article. Feel free to use another name, but be careful to use that name consistently throughout your project. Change to the pytweetproj directory. To start the Django Web server, at the command line, type python manage.py runserver, open a Web browser, and navigate to http://127.0.0.1:8000. You should get a page that says,"It worked!" If at any time you want to stop or restart the server, press Ctrl+C to kill the process. Don't use the development server for production. When you've finished development on your project, move it over to a secure production server, such as Apache.

While in the root project directory, create a subdirectory called resources, which will contain your CSS file, JavaScript files, and any other media. From wherever you extracted the previously downloaded ThemeRoller file, move or copy the css and js directories (extracted from the ThemeRoller ZIP file), as well as the jQuery core to the resources directories.

With your preferred text editor, open the settings.py file and change it to match the settings provided in Listing 4.

Listing 4. Django settings
# Django settings for pytweetproj project.
import os
APPLICATION_DIR = os.path.dirname( globals()[ '__file__' ] )

DEBUG = True
TEMPLATE_DEBUG = DEBUG

ADMINS = (
)

MANAGERS = ADMINS

# we'll use sqlite3 for caching
DATABASE_ENGINE = 'sqlite3'           
DATABASE_NAME = 'session.db'             
DATABASE_USER = ''             
DATABASE_PASSWORD = ''         
DATABASE_HOST = ''             
DATABASE_PORT = ''             

TIME_ZONE = 'America/Chicago'
LANGUAGE_CODE = 'en-us'

SITE_ID = 1

# if you won't be using internationalization, better to keep
# it false for speed
USE_I18N = False

MEDIA_ROOT = os.path.join( APPLICATION_DIR, 'resources' )
MEDIA_URL = 'http://localhost:8000/resources/'
ADMIN_MEDIA_PREFIX = '/media/'

# Make this unique, and don't share it with anybody.
SECRET_KEY = '=y^moj$+yfgwy2kc7^oexnl-f6(b#rkvvhq6c-ckks9_c#$35'

# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.load_template_source',
    'django.template.loaders.app_directories.load_template_source',
#     'django.template.loaders.eggs.load_template_source',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware', 
    'django.contrib.auth.middleware.AuthenticationMiddleware',
) 

ROOT_URLCONF = 'pytweetproj.urls'

TEMPLATE_DIRS = (
     os.path.join( APPLICATION_DIR, 'templates' ), 
)

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'twitterPanel',
)

# added for sessions
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'

To get the path of the current working directory in an operating system-independent manner, use os.path.dirname( globals()[ '__file__' ] ) to set the application directory. You use the same method to set the path to the resources directory, as well as the TEMPLATE_DIR. It's also necessary to set the MEDIA_URL to http://localhost:8000/resources/.

You will be using sqlite3, a database included in Python. You will also need to set the DATABASE_ENGINE, DATABASE_NAME, and the ROOT_URLCONF variables. Notice that INSTALLED_APPS contains a value called twitterPanel, which isn't created yet: You'll create that Django application next. Invoke the command python manage.py syncdb to create the session database.

To create the twitterPanel application, use django-admin startapp twitterPanel and type cd to change into the newly created module directory: twitterPanel. In the twitterPanel directory, create another directory called templates, which will contain the Django templates.


Crafting your URLs

With your favorite text editor, open the file url.py in the root of your project and change the code to match Listing 5. This file maps URLs to callback functions.

Listing 5. Root URL settings
from django.conf.urls.defaults import *
from django.conf import settings

urlpatterns = patterns('',
    ( r'^resources/(?P<path>.*)$', 
      'django.views.static.serve', 
      { 'document_root': settings.MEDIA_ROOT } ),
    ( r'^twitterPanel/', include( 'twitterPanel.urls' ) ),
)

Regular expressions

According to Wikipedia, regular expressions "provide a concise and flexible means for identifying strings of text of interest, such as particular characters, words, or patterns of characters." If you don't have any experience with regular expressions, I highly recommend Mastering Regular Expressions by Jeffrey Friedl or Text Processing in Python by David Mertz. The Regular Expression HOWTO found at the Python documentation page is also an excellent starting point.

The first mapping in urlpatterns defines a match for statically served resources or media — for example, image files. Because you are using the Django development server, to serve your static content, you use the django.views.static.serve() view. The second mapping includes the URL patterns found in the twitterPanel application. Open the url.py file found under twitterPanel (don't confuse that url.py with the one in root) and add the code in Listing 6.

Listing 6. Twitter panel URLs
from django.conf.urls.defaults import *
from twitterPanel.views import *

urlpatterns = patterns( '',
    url( r'^$', index, name = 'twitterPanel_index' ),
    url( r'^panel/$', panel, name = 'twitterPanel_panel' ),
    url( r'^update/$', update, name = 'twitterPanel_update' ),
)

The views

You will create just three views: index, panel, and update. Notice that you will not add views to the root project — only to the Twitter Panel application. Open the view.py file found in the twitterPanel directory and add the imports in Listing 7 to the top of the file.

Listing 7. Twitter panel view imports
from django.shortcuts import render_to_response
from django.template.context import RequestContext
import twitter

The render_to_response() method is a Django shortcut for rendering a template with a specific context and returns an HttpResponse object. The RequestContext is a Django context with a few differences from the regular django.template.Context. Specifically, it accepts an HttpRequest object, and it automatically populates the context with variables found in the TEMPLATE_CONTEXT_PROCESSORS setting.

The first view method, index(), should look like Listing 8.

Listing 8. The index view
def index(request):
    template = 'index.html'
    userName = request.POST.get( 'userTxt' )
    password = request.POST.get( 'passwordTxt' )

    if userName is not None and password is not None:        
        try:
            api = twitter.Api( username = userName, password = password) 
            # just grab the first 5 tweets
            statuses = api.GetUserTimeline()[0:5] 
            # limit to just the first 120
            following = api.GetFriends()[0:120] 
        except NameError, e:
            print "unable to login"

    else:
        statuses = {}
        following = False 

    # place values in session
    request.session['statuses'] = statuses
    request.session['following'] = following
    request.session['userName'] = userName
    request.session['password'] = password
    
    data = {
        'statuses' : statuses,
        'following' : following,
    }

    return render_to_response(template, data, 
           context_instance = RequestContext(request))

The template, index.html, hasn't been written yet. For the moment, just remember that you have two HTML elements: a text box for entering the user name and a password input. The data is sent to the view through the POST method. If a user name and password are entered, you instantiate a twitter.Api object and retrieve the first five tweets of the logged-in user, as well as the first 120 people you're following. The statuses, friends, user name, and password are all stored in the session for retrieval later from other parts of the program.

The second view method, panel(), is much shorter. It's shown in Listing 9.

Listing 9. The panel view
def panel(request):
    template = 'panel.html'
    data = {
        'statuses' : request.session['statuses'],
        'following' : request.session['following']
    }
    return render_to_response(template, data,
           context_instance = RequestContext(request))

Here, you use the panel.html template, also nonexistent as of yet. The panel is where all the action will be going on after you're logged in — in other words, where the tweets are displayed, updates get done, and your friends are displayed. More on that in a little bit. For the moment, all you're doing here is getting some data from the session, then doing a render_and_response. In contrast, the third view method, update, is a bit more complex. See Listing 10.

Listing 10. The update view
def update(request):
    template = 'panel.html'
  
    userName = request.session['userName']
    password = request.session['password']
 
    try:
        api = twitter.Api( username = request.session['userName'], 
                           password = request.session['password']) 
        api.PostUpdate(request.POST.get('updateTextArea'))
        updated = True
        statuses = api.GetUserTimeline()[0:5] # reload the statuses
    except NameError, e:
        print "unable to login"
        updated = False

    data = {
        'userName' : request.session['userName'],
        'password' : request.session['password'],
        'updated'  : updated,
        'statuses' : statuses
    }
    return render_to_response(template, data,
           context_instance = RequestContext(request))

The user name and password are taken from the session, used to instantiate another twitter.Api object, then use the api object to post an update to the account. The statuses are then reloaded.


The templates

Within twitterPanel/templates, create a file called index.html and open it. Fill it with the code in Listing 11.

Listing 11. The index template
<html>
  <head>
    <title>Sample Python-Twitter Application</title>
    <link type="text/css" 
        href="{{MEDIA_URL}}css/cupertino/jquery-ui-1.7.2.custom.css" 
        rel="stylesheet" />
    <script type="text/javascript" 
        src="{{MEDIA_URL}}js/jquery-1.3.2.min.js"></script>
    <script type="text/javascript" 
        src="{{MEDIA_URL}}js/jquery-ui-1.7.2.custom.min.js"></script>
    <script type="text/javascript">

      $(function(){
        // Dialog
        $('#dialog').dialog({
          autoOpen: false,
          width: 400,
          buttons: {
            "Ok": function() {
              $(this).dialog("close");

              $.post('{% url twitterPanel_index %}', $("#loginForm").serialize()); 
              $( '#panel' ).html( '&nbsp;' ).load( '{% url twitterPanel_panel %}' )

              $( '#dialog_link' ).fadeOut(1500);
              $( '#demoTitle' ).fadeOut(1500);

            },
            "Cancel": function() {
              $(this).dialog("close");
            }
          }
        });

        // Dialog Link
        $('#dialog_link').click(function(){
          $('#dialog').dialog('open');
          return false;
        });
        //hover states on the static widgets
        $('#dialog_link, ul#icons li').hover(
          function() { $(this).addClass('ui-state-hover'); },
          function() { $(this).removeClass('ui-state-hover'); }
        );
      });
    </script>
    <style type="text/css">
      body{ font: 62.5% "Trebuchet MS", sans-serif; margin: 50px;}
      .demoHeaders { margin-top: 2em; }
      #dialog_link {padding: .4em 1em .4em 20px; 
         text-decoration:none; position: relative;}
      #dialog_link span.ui-icon {
         margin: 0 5px 0 0;position: absolute; 
         left: .2em;top: 50%;margin-top: -8px;}
      ul#icons {margin: 0; padding: 0;}
      ul#icons li {
         margin: 2px; position: relative; padding: 4px 0; 
         cursor: pointer; float: left;  list-style: none;}
      ul#icons span.ui-icon {float: left; margin: 0 4px;}
    </style>
  </head>

  <body>

  <h2 id="demoTitle" class="demoHeaders">PyTwitter Demo</h2>
  <p>
    <a href="#" id="dialog_link" class="ui-state-default ui-corner-all">
      <span class="ui-icon ui-icon-newwin"></span>Start Demo
    </a>
  </p>

  <div style="position: relative; width: 96%; height: 200px; padding:1% 4%; 
       overflow:hidden;" class="fakewindowcontain">
  </div>


  <!-- ui-dialog -->
  <div id="dialog" title="Login" >
    <p>
      <form action="#" name="loginForm" id="loginForm" method="POST">
        <table>
          <tr>
            <td>user</td> <td><input type="text" name="userTxt" id="userTxt"/></td>
          </tr>
          <tr>
            <td>password</td> 
            <td><input type="password" name="passwordTxt" id="passwordTxt"/></td>
          </tr>
        </table>
      </form>
    </p>
  </div>

  <!-- only show if logged in -->
  <!-- Accordion -->
  <div id="panel" style="position: absolute; top: 2%; left:2%; 
     width:600px; visibility:visible;"> 
  </div>
                
  </body>
</html>

Figure 2 displays the start of the application: a jQuery UI button.

Figure 2. jQuery UI button
Screenshot displays jQuery UI button

This button calls the Login window shown in Figure 3.

Figure 3. The Login window
Figure 3 shows the login window

Let's dissect the code in Listing 11. First, you can tell the browser where the CSS, JavaScript, and jQuery files are found using the {{MEDIA_URL}}. When Django renders the template, it substitutes {{MEDIA_URL}} with the actual path.

Inside the header, add your own JavaScript. The dollar sign ($) is an alias for a jQuery instance. A jQuery shortcut for document.getElementById('name') is $('#name'). For example, you define a jQuery UI element dialog using a <div> tag, and then call the .dialog() method to create the jQuery window. Inside, you add two buttons: OK and Cancel. Cancel closes the window, whereas OK performs two actions: It closes the window, then posts to the server. If the data that the index view receives is the correct credentials, you get back the tweets and followers data. Then, the window button and title fade out, and the Twitter panel fades in.

The magic line here is $( '#panel' ).html( '&nbsp;' ).load( '{% url twitterPanel_panel %}' ), which loads the external Django template panel.html. The Twitter panel is a jQuery UI accordion widget. Listing 12 shows the code for the panel template. Save the code into panel.html inside within twitterPanel/templates.

Listing 12. The Twitter panel template
  <script type="text/javascript">
    $(function(){
      $("#main").accordion({
        header: "h3",
        autoHeight: true
      })
      .hide()
      .fadeIn(1500);

      $('#updateTextArea').keyup(function(){ 
        // text used for non-input elements. Var() is used for input elements
        var tweetLength =  $('#updateTextArea').val().length;
        $('#charCount').text( 140 - tweetLength );
      });

      $('#updateBtn').click(function(){
        if($('#updateTextArea').val().length <= 140){
          // submit 
          $.post('{% url twitterPanel_update %}', $("#updateForm").serialize()); 
          $("#updateStatus").text("last update: " + $('#updateTextArea').val());
          // clear text area
          $('#updateTextArea').val(""); 
          // reset counter too
          $('#charCount').text( "140" );
        } 
        return false;
      });
    });
  </script>                        
  
  <div id="main">
    <div>
      <h3><a href="#">My Recent Tweets</a></h3>
      <div>
        {% if statuses %}
          {% for s in statuses %}
            <p style="border-top:1px dashed #f00; margin-top:1em; 
                padding-top:1em font: 80% 'Trebuchet MS', sans-serif;">
              {{ s.text }}
            </p>
          {% endfor %}
        {% else %}
          No tweets found
        {% endif %}
      </div>
    </div>
    <div>
      <h3><a href="#">Update Status</a></h3>
      <div>
        <form action="#" id="updateForm" name="updateForm" method="POST">
          <textarea id="updateTextArea" name="updateTextArea" rows=2 cols=70 />
          <button id="updateBtn" class="ui-state-default ui-corner-all">Update</button>
        </form>
        <div id="charCount" name="charCount" value=140>140</div>
        <div id="updateStatus" name="updateStatus"><!-- update message here --></div>
      </div>
    </div>
    <div>
      <h3><a href="#">Following</a></h3>
      <div>
        {% if following %}
          {% for f in following %}
            <img src="{{ f.GetProfileImageUrl }}" width=28 height=28 />
          {% endfor %}
        {% else %}
          following variable is empty
        {% endif %}
      </div>
    </div>
  </div>

Figure 4 shows the Twitter panel with the Update Status tab.

Figure 4. The Twitter panel displaying the Update Status tab
Figure 4 shows screenshot of Twitter panel with Update Status tab

You name the accordion widget "main," and this is where all the action is once you're logged in. Inside the accordion, you have three tabs: My Recent Tweets, Update Status, and Following, as shown in Figure 5.

Figure 5. The Twitter panel, displaying the Following tab
Figure 5 shows screenshot of Twitter panel, displaying the Following tab

The first tab displays just the first five tweets. If the status object isn't empty, Django loops through each status object and displays the corresponding text. Otherwise, a "No tweets found" message is printed inside the tab.

The second tab, Update Status, contains an HTML form with a text area and button. Under the form is a <div> called charCount, followed by another <div> called updateStatus. Update Status is given an onclick event; the message length is tested to make sure it's less than the 140-character limit and, if so, uses the jQuery post method to send to the update view the update text, which takes care of the rest. The latest tweet is then displayed on the page.

Just for fun, I've added some JavaScript for counting the number of characters left in the tweet. Remember, Twitter has a 140-character limit per tweet. The way it works is, when a key is released, the number of characters found in the update text box is subtracted from 140. The character count is then updated on the page.

The third tab in the Twitter panel, Following, first checks whether the following object is not null. If so, just like the My Recent Tweets tab, it loops through each user object. Each user has a profile image URL: You stick that value inside the src attribute of an <image> tag. The result is that each user you're "following" is displayed in the third tab.

Now that all the pieces are in place, start the Django server and go to http://127.0.0.1:8000/twitterPanel. You should see a jQuery UI button for logging in. After clicking the button, the Login window appears. Type your twitter credentials, and the Twitter panel should fade in.


Conclusion

Now that you've looked at the demo, you've probably noticed how much effect the jQuery UI adds to the dynamic feel of the application. Django also makes it easy to handle the Python back end and templating.

Other things you could do to improve on the application include adding proper validation to the login process, error windows, or a list of your followers; sending direct tweets; and searching for tweets. Of course, feel free to use your creativity, as well.


Download

DescriptionNameSize
Sample codeos-social-twitter-pytweetproj.zip160KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=447606
ArticleTitle=Build a Twitter Web application
publish-date=12032009