Fixing Django’s feed generator without hacking Django
I installed security update 1.4.1 for Django yesterday, and when I went to hack feedgenerator.py I thought I’d take another look at somehow subclassing or otherwise overriding the offending code. It’s been a long time since I wrote that hack and maybe I’ve learned enough about Django and/or Python to stop having to hack Django’s source every time I upgrade.
The offending code is in add_item_elements in django.utils.feedgenerator.Rss201rev2Feed. When creating a feed, however, I don’t subclass Rss201rev2Feed, I subclass django.contrib.syndication.views.Feed. In fact, all of my feeds inherit from a base subclass called NSFeed.1
Feed uses Rss201rev2Feed by way of DefaultGenerator. It’s just a property, feed_type, on the Feed class. So I overrode the feed_type property with my own subclass of Rss201rev2Feed and was able to override add_item_elements. I tested it by just putting in one line, “pass”, and checking the feed contents; it was just a bunch of empty items, as hoped for. Replacing “pass” with a “super” call to get the parent method’s functionality restored the feed.
Unfortunately, add_item_elements does a lot of work—it adds everything via a series of if/then statements. It uses an XMLGenerator subclass—the “handler” variable—to add elements to itself depending only on the dict entries in the “item” variable. My first thought was to let the parent add_item_elements do its work and then just add the isPermaLink attribute to the newly-added guid element. As far as I can tell, however, XMLGenerator is focused purely on XML generation, with no methods for XML modifications.
Fortunately guid is an optional element. If it doesn’t exist in the item dict, add_item_elements doesn’t create one. So I can modify handler before passing it through to the parent and then set guid to None. The element already has a guid element with isPermaLink=False and the parent doesn’t add another.
Note that as far as I can tell, none of these classes are documented beyond their signature, so they’re likely subject to change in any Django revision.
[toggle code]
- from django.contrib.syndication.views import Feed
- from django.utils.feedgenerator import Rss201rev2Feed
- import hashlib
-
class NSFeedGenerator(Rss201rev2Feed):
- #in my feeds, no guid is used as a link
-
def add_item_elements(self, handler, item):
-
if item['unique_id'] is not None:
- handler.addQuickElement(u"guid", item['unique_id'], {u"isPermaLink": "false"})
- item['unique_id'] = None
- super(NSFeedGenerator, self).add_item_elements(handler, item)
-
if item['unique_id'] is not None:
-
class NSFeed(Feed):
- feed_type = NSFeedGenerator
- type = "application/rss+xml"
- description_template = "feeds/latest_description.html"
-
def item_pubdate(self, obj):
- return max(obj.edited, obj.added)
-
def item_guid(self, obj):
- hasher = hashlib.md5()
- url = unicode(obj.get_absolute_url())
- edited = unicode(obj.edited)
- hashString = url + edited
- hashString = hashString.encode('utf-8')
- hasher.update(hashString)
- hash = hasher.hexdigest()
- return hash
This simple subclass of Feed ensures that all objects get a unique id from a hash of when they were last changed and their URL. The NSFeedGenerator subclass of Rss201rev2Feed ensures that no guids are treated as permalinks, because all of my feeds can have the same URL come up multiple times; using the URL as the guid would mean that browsers would ignore subsequent listings. Your own logic may vary, so you would need to replace my logic (if unique_id exists, make isPermaLink false) with your own.
In response to Django 1.0 feedgenerator and unique IDs: The new Django now supports unique_id, but still doesn’t check to see if it’s a permalink or not.
For Negative Space, not for Apple’s NextStep frameworks.
↑
- Django syndication feed framework
- “Django comes with a high-level syndication-feed-generating framework that makes creating RSS and Atom feeds easy. To create any syndication feed, all you have to do is write a short Python class. You can create as many feeds as you want.”
- Django Utils
- “This document covers all stable modules in django.utils. Most of the modules in django.utils are designed for internal use and only the following parts can be considered stable and thus backwards compatible as per the internal release deprecation policy.”
- django/utils/feedgenerator.py
- The Django SyndicationFeed generator.
- xml.sax.saxutils — SAX Utilities
- “The module xml.sax.saxutils contains a number of classes and functions that are commonly useful when creating SAX applications, either in direct use, or as base classes.”
More Django
- Converting an existing Django model to Django-MPTT
- Using a SQL database to mimic a filesystem will, eventually, create bottlenecks when it comes to traversing the filesystem. One solution is modified preordered tree traversal, which saves the tree structure in an easily-used manner inside the model.
- Two search bookmarklets for Django
- Bookmarklets—JavaScript code in a bookmark—can make working with big Django databases much easier.
- ModelForms and FormViews
- This is just a notice because when I did a search, nothing came up. Don’t use ModelForm with FormView, use UpdateView instead.
- Django: fix_ampersands and abbreviations
- The fix_ampersands filter will miss some cases where ampersands need to be replaced.
- Custom managers for Django ForeignKeys
- I’ve got one really annoying model for keywords. There’s one category of keywords that, by default, should not show up when used as a ForeignKey for most models. Key word: most.
- 29 more pages with the topic Django, and other related pages
More RSS
- No more Twitter on masthead
- Yes, my Twitter feed is off of the masthead, due to the lack of an easy RSS feed.
- Why I still use RSS
- I still use RSS because connections regularly fail, especially to Twitter.
- Using ETag and If-Modified-Since
- In the article on grabbing an RSS feed, I mentioned that if you’re grabbing a feed more than once a day, you should pay attention to the ETag and the If-Modified-Since headers. Here’s how to do that.
- Django Twitter tag and RSS object
- I wanted to embed my twitter feed into my Django blog, and didn’t see any simple RSS readers for Python that did what I wanted.