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.

Four comments
Great, I basically did the same thing today. :) The only difference beeing, that I wanted a feed of all comments associated to my blog. I took inspiration from the existing comments feed of django.contrib.
I'm sorry, I only wanted to check the preview, not post the comment. Anyways, what I wanted to say:
The original code is checking for is_removed too, so if you're using this feature, you might want to include that in your query.
return filter(is_public=True, is_removed=False)@Reiner
Good tip! I adjusted my feed today to include
is_removed=False.Awesome. thank you!
Comments no longer accepted for this entry.
To prevent spam, comments are no longer allowed after 60 days.