RSS feed for comments on a Django blog entry

Django makes it possible to have an RSS feed for everything. Of course, possible is not always easy.

Sometimes it just takes a little logic, documentation and the help of programmers much smarter than I to get things done. In this case, it means building an RSS feed for comments on individual entries on my Django blog.

Prerequisites

I’m using Django 1.0.2 and the contrib comments and syndication applications for this example.

Creating the feed

The first thing I’d recommend is checking out the Django Project documentation for creating a complex feed. This will show you how to derive objects related to another object (comments for an entry).

My Django blog models.py is pretty basic:

class Entry(models.Model):
# Status options
CLOSED_STATUS = 1
EDITING_STATUS = 2
LIVE_STATUS = 3
STATUS_CHOICES = (
    (CLOSED_STATUS, 'Closed'),
    (EDITING_STATUS, 'Editing'),
    (LIVE_STATUS, 'Public'),
)
# Title and slug fields
title = models.CharField(max_length=200, help_text='This field will populate the slug field. Maximum 200 characters.')
slug = models.SlugField(unique_for_date='pub_date')

# Summary and body fields
summary = models.TextField(help_text='Please use <a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a>.')
body = models.TextField(help_text='Please use <a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a>.')

# Markdown conversion for summary and body fields
summary_html = models.TextField(editable=False, blank=True)
body_html = models.TextField(editable=False, blank=True)

# Tag field
tags = models.ManyToManyField(Tag, blank=True)

# Meta fields
meta_keywords = models.CharField(blank=True, max_length=300, help_text='Comma-separated list of keyworks for this entry. Maximum 300 characters.')
meta_description = models.CharField(blank=True, max_length=400, help_text='A brief description of this entry. Maximum 400 characters.')

# Image field
centerpiece_image = models.ForeignKey(Photo, blank=True, null=True)

# Response link field
response_link = models.ForeignKey(Link, blank=True, null=True)

# Date fields
pub_date = models.DateTimeField(default=datetime.datetime.now)
update = models.DateTimeField(blank=True, editable=True, auto_now=False, null=True)

# Author field
author = models.ForeignKey(User)

# Enable comments field
enable_comments = models.BooleanField(default=True)

# Entry status field
status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, default=EDITING_STATUS)

# Entry managers
objects = models.Manager()
live = LiveEntryManager()

class Meta:
    ordering = ['-pub_date']
    verbose_name_plural = 'entries'

def __unicode__(self):
    return self.title

def save(self):
    self.summary_html = markdown(self.summary)
    self.body_html = markdown(self.body)
    super(Entry, self).save()
    try:
        ping_google()
    except Exception:
        pass

def get_absolute_url(self):
    return '/blog/%s/%s/' % (self.pub_date.strftime('%Y/%b/%d').lower(), self.slug)

@property
def comments_expired(self):
    delta = datetime.datetime.now() - self.pub_date
    return delta.days < 60

I basically want to get an entry, ensure I only get the slug, and then grab the comments for that entry as the items in my feed. In this way, it plays very much like the Django project example with crimes for a beat.

Here’s my code:

from django.contrib.syndication.feeds import Feed, FeedDoesNotExist
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.comments.models import Comment
from django.contrib.sites.models import Site

current_site = Site.objects.get_current()

class CommentsForEntry(Feed):
    def get_object(self, bits):
        if len(bits) != 1:
            raise ObjectDoesNotExist
        return Entry.objects.get(slug__exact=bits[0])

    def title(self, obj):
        return "Comments posted on the entry %s | %s" % (obj.title, current_site.name)

    def link(self, obj):
        if not obj:
            raise FeedDoesNotExist
        return obj.get_absolute_url()

    def description(self, obj):
        return "Comments posted on the entry %s" % obj.title

    def items(self, obj):
        return Comment.objects.for_model(obj).filter(is_public=True).order_by('-submit_date')[:15]

My first attempt at writing this failed to use the last bit of code, return Comment.objects.for_model(obj) which returns the comments for the object defined (my entry). I added filtering for only public comments in addition to changing how the comments are displayed in the feed.

After adding this code to your feeds.py put the following in your project urls.py:

from yourproject.apps.blog.feeds import CommentsForEntry

feeds = {
    'entry-comments': CommentsForEntry,
}

urlpatterns = patterns('',
    (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}),
)

After making these changes and restarting your Apache instance, you should be able to see your RSS feed for comments on an entry by going to the following URL: http://sitename.com/feeds/entry-comments/<entry_slug>/

Please post your suggestions on improving this feed in the comments.

Read full article at “Entries for Django tag | patrickbeeson.com”

Leave a comment