Django recipe: Make an RSS feed for every tag

By

This afternoon I made a small update to the site, adding RSS feeds for every tag in the database. So now, if you like, you can subscribe to the latest content about python or music or media or anything else I've got. The feeds are found in the hidden autodiscovery tag at the top of the page, so you'll have to use the pulldown in your browser's URL bar to get at them. But they're there.

Thanks to a number of shortcuts in Django's feed syndication framework and the excellent django-tagging, it's pretty easy to put something like this together. Here's how I did it.

First, in my blog app's feeds.py file:

# Feeds
from django.contrib.syndication.feeds import Feed, FeedDoesNotExist

# Models
from tagging.models import *

# Helpers
from django.core.exceptions import ObjectDoesNotExist


class TagFeed(Feed):
    """
    The most recent content with a particular tag.
    """
    
    def get_object(self, bits):
        """
        Fetch the Tag object.
        """
        if len(bits) != 1:
            raise ObjectDoesNotExist
        return Tag.objects.get(name__exact=bits[0])
        
    def title(self, obj):
        """
        Set the feed title.
        """
        return "%s . tags . palewire" % obj.name.lower()

    def description(self, obj):
        """
        Set the feed description.
        """
        return "the latest tagged %s" % obj.name.lower()

    def link(self, obj):
        """
        Set the feed link.
        """
        if not obj:
            raise FeedDoesNotExist
        return u'/tags/%s/' % obj.name
        
    def items(self, obj):
        """
        Fetch the latest 10 objects with a particular tag, which is passed as the `obj` argument.
        """
        # Pull all the items with that tag.
        taggeditem_list = obj.items.all()
        # Loop through the tagged items and return just the items with a pub_date attribute
        object_list = [i.object for i in taggeditem_list if getattr(i.object, 'pub_date', False)]
        # Now resort them by the pub_date attribute with the newest coming first
        object_list.sort(key=lambda x: x.pub_date, reverse=True)
        # And return the first ten.
        return object_list[:10]
        
    def item_link(self, obj):
        """
        Set the URL for each tagged item, using the url attribute we have on each of our models.
        """
        if not obj:
            raise FeedDoesNotExist
        return obj.url

I then added the new TagFeed class to my feed dictionary in the urls/feeds.py file. You can see it at the bottom there.

# urls
from django.conf.urls.defaults import *

# Feeds
from coltrane.feeds import *

# Feed index page
urlpatterns = patterns('django.views.generic',
    (r'^list/$', 'simple.direct_to_template', {'template': 'coltrane/feed_list.html'}),
)

# RSS feeds mapped to URL slugs
feeds = {
    # Bundles
    'the-full-feed': FullFeed,
    'less-noise': LessNoise,
    # Singletons
    'posts': RecentPosts,
    'comments': RecentComments,
    'shouts': RecentShouts,
    'links': RecentLinks,
    'photos': RecentPhotos,
    'tracks': RecentTracks,
    'books': RecentBooks,
    'commits': RecentCommits,
    'tag': TagFeed,
}

# Feed urlpattern
urlpatterns += patterns('',
    url(r'(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}, name='coltrane_feeds'),
)
</url>

And finally added the URL to the autodiscovery portion of the head in my tag_detail.html template.

{% block extrarss %}
    <link rel="alternate" type="application/rss+xml" title="latest tagged {{ tag }}" href="/feeds/tag/{{ tag }}/" />
{% endblock %}

You can find my blog's entire codebase online here. And I always appreciate a good criticism. So if you see anything wrong, fire away.

en
480