Fun With Django Feeds

Posted on Fri 03 August 2007 in Dispatches • 5 min read

I would like to take a moment to show you how easy it is to create feeds in Django, and the easiest way to do that is by example. For brevity’s sake, I’m going to use a simplified version of a Django-powered blog. So, let’s say you have a simple blog, where posts consist of a title, slug, pub_date, body and some tags. So your model would look something like this:

# blog/models.py
from django.db import models
from tag.fields import TagField
from datetime import datetime

class Entry(models.Model):
    title = models.CharField(maxlength=250)
    slug = models.SlugField(
                maxlength=250,
                prepopulate_from=('title'),
                unique_for_date='pub_date'
    )
    body = models.TextField()
    pub_date = models.DateTimeField(default=datetime.now())
    is_draft = models.BooleanField(default=True)
    allow_comments = models.BooleanField(default=True)
    tags = TagField()

    def __unicode__(self):
        return self.title

    def get_absolute_url(self):
        #You would fill this in here and call it when you need it or
        #use the permalink decorator
        #Regardless, you want to customize it for your urls.py
        pass

Now, I’m assuming you already know something about the Django ORM, but I’ll summarize for those of you that don’t. Essentially, we are creating an object of type Entry, which represents a table in your database and has the attributes I listed above. I’ve added a few constraints, such as requiring the slug field to be unique for a given date, since in most cases you will probably build a permalink along the lines of /year/month/day/slug. I’ve also cheated and added two boolean fields which designate if the entry is a draft, and if it allows comments, because they are too useful to leave out, even in an example. TagField is supplied by the django-tagging app, and doesn’t really affect the rest of this example, but I love it so I’m going to leave it in. ;-)

Okay, so you’ve got your model and you have used it to load up some entries, and now it suddenly occurs to you: What’s a blog without a subscription feed? The answer to that question is: Probably not worth the effort to remember visiting on a regular basis. You need an RSS feed! Luckily for you, Django makes this dead simple.

So, the first order of business is to define a feed class to represent your data. Unless you have very complex needs, this is very simple because you just have to subclass the Feed class in Django. You’ll have to define a few variables and then tell the feed what items it should be displaying and what date is the representative pubdate. So, a simple example might look like this:

# feeds.py
from django.contrib.syndication.views import Feed
from blog.models import Entry

class BlogFeed(Feed):
    '''Feed for latest 15 blog entries'''
    #These could also be pulled from your settings.py file to avoid repetitive hardcoding
    title='My Blog'
    link='http://www.example.com' #URI of site
    description='Latest Blog Entries'

    item_author_name='Joe Blow'
    item_author_email='joe@example.com'
    item_author_link='http://www.example.com' #URI of author

    def items(self):
        #What items to use in the feed
        return Entry.objects.filter(is_draft=False).order_by('-pub_date')[:15]

    def item_pubdate(self,item):
        #For each item, what is the pubdate?
        return item.pub_date

So, that’s a simple feed. Now you need to assign this feed to a url, which you do by adding it to your urls.py file. I’m going to just include the feed urls line rather than including an entire site description.

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

urlpatterns = patterns('',
#Lots of patterns omitted here
(r'^feeds/blog/$', BlogFeed()),
)

This tells Django that for every time it encounters a request beginning with `feeds`, it should take the remainder of the url as a slug and find the corresponding feed class in `site_feeds`. Django now uses class-based views for feeds, so you just need to create an instance of each feed class that will correspond with an assigned url. There is only one step left, making the templates. Don’t worry, this is the easiest part.

Every feed needs two templates, although you can reuse templates by specifying the title_template and description_template in your feed class. Otherwise, Django will look for templates that match your feed’s slug identifier in the templates/feeds directory, where templates is your base directory for templates. In this case it will be looking for blog_title.html and blog_description.html. Both templates get passed one variable: obj, which represents a single item in the feed. Both are ridculously simple:

# blog_title.html
{{ obj.title }}

And:

# blog_description.html
{{ obj.body }}

They are really that simple. You can, of course, apply all your favorite Django template filters or add extra formatting if you wish. If you want to include any other information that you might pull via template tags, you can use those as well. For example if you wanted to also include a comment count your description template would look like this:

# blog_description.html
{% load comments %}
{% get_comment_count for blog.entry obj.id as comment_count %}
{{ obj.body }}
{{ comment_count }} comment{{ comment_count|pluralize }} on this entry.

Note: Django also allows you to setup feeds without templates, but if you want to do any specialized formatting or if you want to incorporate additional data, you will still want to use the template method described above.

By default, Django will serve up your feed as RSS 2.0, however, you can also create Atom feeds just as easily. You can even serve up both types of feeds by subclassing your BlogFeed class like so:

# feeds.py
from django.contrib.syndication.views import Feed
from blog.models import Entry
from django.utils.feedgenerator import Atom1Feed

#For brevity's sake, I'm omitting the BlogFeed I described above.
class AtomBlogFeed(BlogFeed):
    feed_type = Atom1Feed
    subtitle = BlogFeed.description

That’s all there is to it! Simply assign it a feed slug and either create new templates or specify in the AtomBlogFeed class to use the same templates as BlogFeed.

It is possible to completely customize your feeds, but that’s a little out of the scope of this post. This basic approach should work for the majority of cases, and if you need to do something more advanced with your feed, take a moment to browse through the official documentation as there are a wealth of options available to you.

This may initially look like a lot of work, but believe me this takes no time at all. This site generates feeds for every kind of content, including by tag, and tag per category, and I was able to write all of those feeds and have them being served up in about twenty minutes. For a simple example like I am showing here you can be up with feeds in less than ten!

Django makes feed generation quick and easy, and like most aspects of Django, is a joy to work with. Go ahead, take a moment and play with it. If you need an app to work with, you can either flesh out this example or you can go through the official tutorial. Afterwards, I’m pretty sure you’ll be hooked. ;-)

UPDATED ON 11/30/2010: Updated guide to be compatible with Django 1.2.3.