Sunday, July 28, 2013

How to Add an RSS Feed

Python 2.7.5  Django 1.4.5

RSS stands for Rich Site Summery or Really Simple Syndication, often called feed or channel, and provides a summarised text (in XML format) on what is going on on your website.

Here you can find Django official documentation on RSS feeds: https://docs.djangoproject.com/en/1.4/ref/contrib/syndication/.

In general, I find working with XML from Python code a pretty unpleasant experience, but there is no running away from it, almost every project will need one of those. Django build in syndication feed framework will take care of a lot of those unpleasantries, and, within it's own limitations, provides a pretty nice way to create a feed and fast.

Now, I will give a pretty straightforward example, yet above that you will find on Django official docs. If this is your first glance on RSS feeds in Django, you might want to go over the docs first (see a link attached at the beginning of this article).
I wrote this feed for a project I'm working on. In this project I have users, and each user has it's own RSS feed(s). Each channel will have a title, a link, description and an image. The image on each channel will show same thing for all and it is my project logo with a link to main website.

I like my code organised, so I start by creating a feeds.py on the same level as views.py.

In feeds.py (follow the link to view this code in a more readable way on gist):

from django.shortcuts import get_object_or_404
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Rss201rev2Feed

from myproject.myapp.models import Model, Broadcast, \
     BroadcastSocialAccountStatus


OFFICIAL_URL = "http://mainwebsite.com/"
OFFICIAL_LOGO_URL = 'https://s3.amazonaws.com/bucketname/logo.png'


class OfficialFeed(Rss201rev2Feed):
    def add_root_elements(self, handler):
        Rss201rev2Feed.add_root_elements(self, handler)
        handler.startElement(u'image', {})
        handler.addQuickElement(u"url", OFFICIAL_LOGO_URL)
        handler.addQuickElement(u"title", u'MyProject')
        handler.addQuickElement(u"link", OFFICIAL_URL)
        handler.endElement(u"image")


class BSAFeed(Feed):

    feed_type = OfficialFeed

    def get_object(self, request, *args, **kwargs):
        user_slug = request.META['PATH_INFO'].split('/')[2]
        user = get_object_or_404(Model, slug=user_slug)
        return user

    def link(self, obj):
        return '/user/{}/broadcasts/feed/'.format(obj.slug)

    def title(self, obj):
        return "%s Broadcasts Feed" % obj.name

    def description(self, obj):
        return "Updates on %s social media activity" % obj.name

    def items(self, obj):
        return BroadcastSocialAccount.objects.filter(broadcast__user=obj,
                 status=BroadcastSocialAccountStatus.SENT).order_by('-sent_at')

    def item_title(self, item):
        return "{} posted on {}".format(item.broadcast.user.name,
                                     item.social_account.partner)

    def item_description(self, item):
        return item.broadcast.message

    # item_link is only needed if NewsItem has no get_absolute_url method.
    def item_link(self, item):
        return item.get_broadcast_abs_url()

    def item_pubdate(self, item):
        return item.sent_at

Let's take a look at what I've got here:

  • feed_type - this is where you can specify if this will be an RSS feed or an Atom feed. This is also where you can add elements to the channel. In my case I added a logo to all channels, which also is a link to my main website.
  • get_object - object in this case is the feed. If you are familiar with Django class based views, this shouldn't be surprising.
  • link - returns a relative uri to this feed (channel). Relative to what? Relative to your project Site instance. This is also one of the limitations of Django feed framework - it's a bit of a problem working with multiple domains on the same project, especially if you don't have multiple Site's to go with those domains.
  • title - title for this feed.
  • description - a description of the feed.
  • items - this is the content of the feed.  Build in go per Model. If you want to syndicate more than one Model, for example if this is a feed for cars manufactory and you want to have a feed for cars and currently active offers, you will either need to create 2 feeds: one for cars and another for offers, or you will need to query for both cars and offers, and then combine them into one list. This method should return a list of items to present, each will have a title, a description, a link and a publication date.
  • item_title - title of an item.
  • item_description - description of an item.
  • item_link - a link to a full article, a link to where a reader can view the item. If this is a blog post, then this is a link to the post itself.
  • item_pubdate - when this item was published, helps readers see how up to date the content is.

An RSS feed is actually just one of the views on a website, and returns a response in XML format. And every view needs a url of course. 
So, now lets connect this view to a URL, in urls.py:

from django.conf.urls import url
from myproject.myapp.feeds import BSAFeed

urlpatterns = (
  ...
  url(r'^broadcasts/feed/$', BSAFeed(), name='broadcasts_feed'),
)


And that's it. Now I have a feed on my website.

No comments:

Post a Comment