Posted on Dec. 18, 2007 at 12:37 P.M.

Widgets are everywhere! It seems that every blog these days has at least a couple of widgets on their sidebar. Yesterday I realized that I had never written one, and probably more importantly, I really had no concrete idea how they worked. Using Django and a bit of Javascript, it turned out to be quite easy! I'm going to show you the basics of how to write a widget yourself, but if enough people would like the source code to mine, I'll happily open source it.

Before starting on creating a widget, we've got to make sure that it has an API of some sort. In the case of Pownce, there some Community Documentation about the various things that can be done with its API. For a simple widget, all we really need is to be able to fetch some public notes.

Our API call will be: http://api.pownce.com/1.1/public_note_lists.json

Now that we've determined where to get our data, let's parse it and do something with it.

from urllib2 import urlopen
from django.utils import simplejson
from django.template import loader, Context
from django.http import HttpResponse
def pownce_widget(request):
    api_call = "http://api.pownce.com/1.1/public_note_lists.json"
    notes = simplejson.loads(urlopen(api_call).read())['notes']
    t = loader.get_template('powncewidget/widget_content.html')
    c = Context({'notes' : notes})
    return HttpResponse(t.render(c))

Now you may be noticing that I'm not using the simpler django.shortcuts.render_to_response. That's because we're going to use the content that we've rendered here as context for another template which we'll use in a moment. Also note that if you were to do this in a production environment, using a simple urlopen is not considered good practice. See chapter 11 in Mark Pilgrim's excellent Dive Into Python for more information about what to do instead.

Now let's create a template:

<ul id="pownce_widget">
    {% for note in notes %}
        <li><a href="{{ note.permalink }}">{{ note.body|slice:":30" }}</a></li>
    {% endfor %}
</ul>

What we've done here is iterated over the public notes, and simply created an unordered list with the first 30 or less words from the note. Also, we've provided a link to Pownce for each note listed.

If browsers could make cross-domain requests, we'd be done now: you could embed a small Javascript file which would asynchronously request this page and update the DOM accordingly. However, this is not possible, so I've come up with a way to do it instead. I'm not sure if it's a best practice--it sure seems to me like it's not--but it works, which is probably more important anyway.

So let's modify our view:

from urllib2 import urlopen
from django.utils import simplejson
from django.template import loader, Context, RequestContext
from django.http import HttpResponse
from django.shortcuts import render_to_response
def pownce_widget(request):
    api_call = "http://api.pownce.com/1.1/public_note_lists"
    notes = simplejson.loads(urlopen(api_call).read())['notes']
    t = loader.get_template('powncewidget/widget_content.html')
    c = Context({'notes' : notes})
    context = {"widget_content" : t.render(c))}
    return render_to_response("powncewidget/widget.html", context, context_instance=RequestContext(request))

We've taken the rendered output from widget_content.html and used it as context for another template, widget.html, which we return as an HttpResponse to the browser. This doesn't make much sense until we've seen what that widget.html template contains. Here it is:

{% load stripwhitespace %}

var _cssNode = document.createElement('link');
_cssNode.type = 'text/css';
_cssNode.rel = 'stylesheet';
_cssNode.href = '{{ MEDIA_URL }}css/powncewidgetstyle.css';
_cssNode.media = 'screen';
document.getElementsByTagName("head")[0].appendChild(cssNode);
document.write('{{ widget_content|stripwhitespace|safe }}');

What we're doing here is dynamically creating Javascript using Django's templating system which injects your rendered HTML into the page. The first few lines add a new stylesheet to the page, and the last one writes your content to the page. But what is this stripwhitespace filter that we see? Javascript does not like multi-line string declarations such as what widget_content produces. With Django, it's easy to write a simple filter to make it all exist on one line:

from django import template
import re
inbetween = re.compile('>[ \r\n]+<')
newlines = re.compile('\r|\n')
register = template.Library()
def stripwhitespace(value):
    return newlines.sub('', inbetween.sub('><', value))
register.filter('stripwhitespace', stripwhitespace)

With that, we've pretty much finished up on creating our widget. There are lots of customization options from here: GET arguments, different API endpoints, etc. Not only that, but there's lots of room for visual customization using CSS. What we're doing is effectively generating Javascript, so anything that you'd like to do using Javascript is fair game as well.

The result after tweaking for a bit is what you see at the right under my "Widgets" links. Here is a picture in case it stops working for some reason:

http://media.eflorenzano.com/img/powncesample.png

If you'd like to create a pownce widget of your own by simply adding a snippet to your site, I've provided a Pownce Widget Creator for your convenience.

I think that it was fairly easy to do this using Django and Python, and if you've got any tips on best practices, please let me know so that I can code a better widget!

Search

 

Recent Links

  • git-issues: A distributed issue tracker built-in to Git.
  • I predicted this back in March--can't believe a solution has surfaced so soon. It makes so much sense to build in an issue tracker to a revision control system. Since you're working with the code, might as well track the issues in the same system and take advantage of the extra metadata. This is really cool (and as a bonus, it's written in Python) so I hope to see it really grow and flourish!

  • How I Work Daily
  • Daily blog by Kevin Fricovsky. In addition to having some really great content, he has started to post audio interviews with people from the Django community. This is a site to keep an eye on in the coming days and months.

  • django-arcade
  • Demo site for django-arcade, an open source reusable Django app to add new flash games to any django-powered site. Looks very cool for easily creating game portals. It also comes from my future employer.

  • Facebook Chat and Scalability (with Erlang)
  • Eugene Letuchy talks about how they they took Facebook Chat from no users to 70 million users, with the help of Erlang.

  • Simon Willison: The Implications of OpenID
  • I somehow missed this presentation when it came out, but it's an absolutely fantastic overview and defense of OpenID by Simon Willison. If you are in any way interested in what OpenID is and what it can offer, you owe it to yourself to check out this presentation.

  • StupidXML
  • Probably the simplest XML library that I've seen for Python. Sometimes you just want to generate some stupid XML, and this is the perfect tool for the job.

  • See the rest of my links...

Pownce

Badges

  • django badge
  • apache badge
  • GeoURL
  • XFN Friendly
  • Valid HTML 4.01 Transitional