Djangogigs and the feedgenerator module

Update: Sorry about the escaped markup in the community feed. That’s what happens when upgrading your working copy in a rush and not testing everything thoroughly.

It’s been a while since I’ve written any kind of django-related post, but since djangogigs got a good mention by Adrian at PyCon08 I thought I’d better talk about some of the updates - some old, some new - to djangogigs and other django-related things.

A while ago we implemented two commonly requested features: filtering by country and filtered feeds. There’s nothing particularly interesting from a development perspective behind these features. However, we did use the feedgenerator module to create the feed itself, which many may not have come across or used in any way, and which can present a couple of traps for the unwary.

So, as we fell into a couple of these traps - and in case this is of use to others - I’ll outline a skeleton view which handles a RESTful feed URI and builds a feed using the Rss201rev2Feed class.

After reading about creating URI which represent an unordered lists of items in RESTful Web Services we thought we’d try it here. The country list is represented by joining the country ids with a semicolon and sticking it on the end of the URI.

The view is fairly concise, and apart from doing some bits to generate the correct country names based on whether it is the definite article or not (see Wiki), there isn’t really much to it.

We split on the semicolon, perform some simple checking to make sure they’re valid digits and then get the countries from the database.

 def generate_feed(request, url=None):
     """Generates a feed"""
     gig_list = Gig.live.all()
     feed_title = u'All Gigs'
     current_domain = Site.objects.get_current().domain
     if url:
         country_ids = [item for item in url.split(';') if item.isdigit()]
         if country_ids:
             def get_definite(x):
                 return u'the %s' % x.name if x.is_definite else x.name
             feed_title = u'Gigs in %s' % punctuate_word_list(
                                             [get_definite(c) for c
                                             in Country.objects.filter(id__in=country_ids)])
             gig_list = gig_list.filter(country__id__in=country_ids)

A couple of things to note if you’re going to manually create feeds:

  • Make sure that you provide the feed/item’s full URI (not just the relative URI that’s returned from obj.get_absolute_url()). We had a number of users tell us that their feed readers were reporting problems before we fixed this.
  • Make sure that you provide the pubdate for each item. Otherwise, as Malcolm pointed out to us via an email, all entries will appear as new to the feed readers.
  • Provide a unique_id for each item. You can just use the full URI again as it will always be unique. This isn’t a show-stopper, but it is specified in the standards so there’s no reason it shouldn’t be there.
 
    feed = feedgenerator.Rss201rev2Feed(title=u'DjangoGigs.com - %s' % feed_title,
                                        link=u'http://%s%s' % (current_domain, 
                                                                urlresolvers.reverse('public_gig_index')),
                                        description=u'Gigs listed on DjangoGigs.com',
                                        language=u'en')
    for gig in gig_list:
        permalink = u'http://%s%s' % (current_domain, 
                                        gig.get_absolute_url())
        feed.add_item(title=gig.name,
                        link=permalink,
                        description=force_escape(gig.description),
                        pubdate=gig.creation_date,
                        unique_id=permalink)
    response = HttpResponse(mimetype=feed.mime_type)
    feed.write(response, u'utf-8')
    return response

As I’ve said, missing these params off can cause problems for users already subscribed to the feed but since I’ve mentioned it, no one else should have a problem and I may add some documentation for this class to aid others.

Just on a closing note: we’re grateful for the ideas that you guys send our way and unfortunately we haven’t managed to get all the features we want in yet due to other constraints. One thing we have added recently is bcc copying when you send a message to either a gig owner or a developer - a number of people had asked for it and it was only a 5 second change so we threw it in. Due to positive feedback from users, we’ve also added the ability for people to send a donation to support the site if they feel they want to.

Also, Scott and I will be launching another couple of django-powered sites in the very near future: so stay tuned for news.

2008-04-10