summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorAdrian Holovaty <adrian@holovaty.com>2005-11-12 03:44:53 +0000
committerAdrian Holovaty <adrian@holovaty.com>2005-11-12 03:44:53 +0000
commit944de9e9e638bc239b03f67f459ad4abe4673e48 (patch)
treed144b7c68fc2f0a7169aa07d0a91daa246b94473 /django
parente8ae3567393443934d896579dd6d1dfc92bddb95 (diff)
Completely refactored legacy RSS framework to the new django.contrib.syndication package. Also added Atom support, changed the way feeds are registered and added documentation for the whole lot. This is backwards-incompatible, but the RSS framework had not yet been documented, so this should only affect tinkerers and WorldOnline. Fixes #329, #498, #502 and #554. Thanks for various patches/ideas to alastair, ismael, hugo, eric moritz and garthk
git-svn-id: http://code.djangoproject.com/svn/django/trunk@1194 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
-rw-r--r--django/conf/urls/rss.py6
-rw-r--r--django/contrib/syndication/__init__.py0
-rw-r--r--django/contrib/syndication/feeds.py89
-rw-r--r--django/contrib/syndication/views.py26
-rw-r--r--django/core/rss.py223
-rw-r--r--django/utils/feedgenerator.py219
-rw-r--r--django/views/rss/rss.py12
7 files changed, 273 insertions, 302 deletions
diff --git a/django/conf/urls/rss.py b/django/conf/urls/rss.py
deleted file mode 100644
index bb6f84e07d..0000000000
--- a/django/conf/urls/rss.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from django.conf.urls.defaults import *
-
-urlpatterns = patterns('django.views',
- (r'^(?P<slug>[^/]+)/$', 'rss.rss.feed'),
- (r'^(?P<slug>[^/]+)/(?P<param>.+)/$', 'rss.rss.feed'),
-)
diff --git a/django/contrib/syndication/__init__.py b/django/contrib/syndication/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/django/contrib/syndication/__init__.py
diff --git a/django/contrib/syndication/feeds.py b/django/contrib/syndication/feeds.py
new file mode 100644
index 0000000000..c495990c51
--- /dev/null
+++ b/django/contrib/syndication/feeds.py
@@ -0,0 +1,89 @@
+from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
+from django.core.template import Context, loader, Template, TemplateDoesNotExist
+from django.models.core import sites
+from django.utils import feedgenerator
+from django.conf.settings import LANGUAGE_CODE, SETTINGS_MODULE
+
+def add_domain(domain, url):
+ if not url.startswith('http://'):
+ url = u'http://%s%s' % (domain, url)
+ return url
+
+class FeedDoesNotExist(ObjectDoesNotExist):
+ pass
+
+class Feed:
+ item_pubdate = None
+ item_enclosure_url = None
+ feed_type = feedgenerator.DefaultFeed
+
+ def __init__(self, slug):
+ self.slug = slug
+
+ def item_link(self, item):
+ try:
+ return item.get_absolute_url()
+ except AttributeError:
+ raise ImproperlyConfigured, "Give your %s class a get_absolute_url() method, or define an item_link() method in your Feed class." % item.__class__.__name__
+
+ def __get_dynamic_attr(self, attname, obj):
+ attr = getattr(self, attname)
+ if callable(attr):
+ try:
+ return attr(obj)
+ except TypeError:
+ return attr()
+ return attr
+
+ def get_feed(self, url=None):
+ """
+ Returns a feedgenerator.DefaultFeed object, fully populated, for
+ this feed. Raises FeedDoesNotExist for invalid parameters.
+ """
+ if url:
+ try:
+ obj = self.get_object(url.split('/'))
+ except (AttributeError, ObjectDoesNotExist):
+ raise FeedDoesNotExist
+ else:
+ obj = None
+
+ current_site = sites.get_current()
+ link = self.__get_dynamic_attr('link', obj)
+ link = add_domain(current_site.domain, link)
+
+ feed = self.feed_type(
+ title = self.__get_dynamic_attr('title', obj),
+ link = link,
+ description = self.__get_dynamic_attr('description', obj),
+ language = LANGUAGE_CODE.decode()
+ )
+
+ try:
+ title_template = loader.get_template('feeds/%s_title' % self.slug)
+ except TemplateDoesNotExist:
+ title_template = Template('{{ obj }}')
+ try:
+ description_template = loader.get_template('feeds/%s_description' % self.slug)
+ except TemplateDoesNotExist:
+ description_template = Template('{{ obj }}')
+
+ for item in self.__get_dynamic_attr('items', obj):
+ link = add_domain(current_site.domain, self.__get_dynamic_attr('item_link', item))
+ enc = None
+ enc_url = self.__get_dynamic_attr('item_enclosure_url', item)
+ if enc_url:
+ enc = feedgenerator.Enclosure(
+ url = enc_url.decode('utf-8'),
+ length = str(self.__get_dynamic_attr('item_enclosure_length', item)).decode('utf-8'),
+ mime_type = self.__get_dynamic_attr('item_enclosure_mime_type', item).decode('utf-8'),
+ )
+ feed.add_item(
+ title = title_template.render(Context({'obj': item, 'site': current_site})).decode('utf-8'),
+ link = link,
+ description = description_template.render(Context({'obj': item, 'site': current_site})).decode('utf-8'),
+ unique_id = link,
+ enclosure = enc,
+ pubdate = self.__get_dynamic_attr('item_pubdate', item),
+ )
+ return feed
diff --git a/django/contrib/syndication/views.py b/django/contrib/syndication/views.py
new file mode 100644
index 0000000000..3236f9dfab
--- /dev/null
+++ b/django/contrib/syndication/views.py
@@ -0,0 +1,26 @@
+from django.contrib.syndication import feeds
+from django.core.exceptions import Http404
+from django.utils.httpwrappers import HttpResponse
+
+def feed(request, url, feed_dict=None):
+ if not feed_dict:
+ raise Http404, "No feeds are registered."
+
+ try:
+ slug, param = url.split('/', 1)
+ except ValueError:
+ slug, param = url, ''
+
+ try:
+ f = feed_dict[slug]
+ except KeyError:
+ raise Http404, "Slug %r isn't registered." % slug
+
+ try:
+ feedgen = f(slug).get_feed(param)
+ except feeds.FeedDoesNotExist:
+ raise Http404, "Invalid feed parameters. Slug %r is valid, but other parameters, or lack thereof, are not." % slug
+
+ response = HttpResponse(mimetype='application/xml')
+ feedgen.write(response, 'utf-8')
+ return response
diff --git a/django/core/rss.py b/django/core/rss.py
deleted file mode 100644
index 7ba0c1e30c..0000000000
--- a/django/core/rss.py
+++ /dev/null
@@ -1,223 +0,0 @@
-from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
-from django.core.template import Context, loader, Template, TemplateDoesNotExist
-from django.models.core import sites
-from django.utils import feedgenerator
-from django.conf.settings import LANGUAGE_CODE, SETTINGS_MODULE
-
-def add_domain(domain, url):
- if not url.startswith('http://'):
- url = u'http://%s%s' % (domain, url)
- return url
-
-class FeedDoesNotExist(ObjectDoesNotExist):
- pass
-
-class Feed:
- item_pubdate = None
- item_enclosure_url = None
-
- def item_link(self, item):
- try:
- return item.get_absolute_url()
- except AttributeError:
- raise ImproperlyConfigured, "Give your %s class a get_absolute_url() method, or define an item_link() method in your RSS class." % item.__class__.__name__
-
- def __get_dynamic_attr(self, attname, obj):
- attr = getattr(self, attname)
- if callable(attr):
- try:
- return attr(obj)
- except TypeError:
- return attr()
- return attr
-
- def get_feed(self, url=None):
- """
- Returns a feedgenerator.DefaultRssFeed object, fully populated, for
- this feed. Raises FeedDoesNotExist for invalid parameters.
- """
- if url:
- try:
- obj = self.get_object(url.split('/'))
- except (AttributeError, ObjectDoesNotExist):
- raise FeedDoesNotExist
- else:
- obj = None
-
- current_site = sites.get_current()
- link = self.__get_dynamic_attr('link', obj)
- link = add_domain(current_site.domain, link)
-
- feed = feedgenerator.DefaultRssFeed(
- title = self.__get_dynamic_attr('title', obj),
- link = link,
- description = self.__get_dynamic_attr('description', obj),
- language = LANGUAGE_CODE.decode()
- )
-
- try:
- title_template = loader.get_template('rss/%s_title' % self.slug)
- except TemplateDoesNotExist:
- title_template = Template('{{ obj }}')
- try:
- description_template = loader.get_template('rss/%s_description' % self.slug)
- except TemplateDoesNotExist:
- description_template = Template('{{ obj }}')
-
- for item in self.__get_dynamic_attr('items', obj):
- link = add_domain(current_site.domain, self.__get_dynamic_attr('item_link', item))
- enc = None
- enc_url = self.__get_dynamic_attr('item_enclosure_url', item)
- if enc_url:
- enc = feedgenerator.Enclosure(
- url = enc_url.decode('utf-8'),
- length = str(self.__get_dynamic_attr('item_enclosure_length', item)).decode('utf-8'),
- mime_type = self.__get_dynamic_attr('item_enclosure_mime_type', item).decode('utf-8'),
- )
- feed.add_item(
- title = title_template.render(Context({'obj': item, 'site': current_site})).decode('utf-8'),
- link = link,
- description = description_template.render(Context({'obj': item, 'site': current_site})).decode('utf-8'),
- unique_id = link,
- enclosure = enc,
- pubdate = self.__get_dynamic_attr('item_pubdate', item),
- )
- return feed
-
-# DEPRECATED
-class FeedConfiguration:
- def __init__(self, slug, title_cb, link_cb, description_cb, get_list_func_cb, get_list_kwargs,
- param_func=None, param_kwargs_cb=None, get_list_kwargs_cb=None, get_pubdate_cb=None,
- enc_url=None, enc_length=None, enc_mime_type=None):
- """
- slug -- Normal Python string. Used to register the feed.
-
- title_cb, link_cb, description_cb -- Functions that take the param
- (if applicable) and return a normal Python string.
-
- get_list_func_cb -- Function that takes the param and returns a
- function to use in retrieving items.
-
- get_list_kwargs -- Dictionary of kwargs to pass to the function
- returned by get_list_func_cb.
-
- param_func -- Function to use in retrieving the param (if applicable).
-
- param_kwargs_cb -- Function that takes the slug and returns a
- dictionary of kwargs to use in param_func.
-
- get_list_kwargs_cb -- Function that takes the param and returns a
- dictionary to use in addition to get_list_kwargs (if applicable).
-
- get_pubdate_cb -- Function that takes the object and returns a datetime
- to use as the publication date in the feed.
-
- The three enc_* parameters are strings representing methods or
- attributes to call on a particular item to get its enclosure
- information. Each of those methods/attributes should return a normal
- Python string.
- """
- self.slug = slug
- self.title_cb, self.link_cb = title_cb, link_cb
- self.description_cb = description_cb
- self.get_list_func_cb = get_list_func_cb
- self.get_list_kwargs = get_list_kwargs
- self.param_func, self.param_kwargs_cb = param_func, param_kwargs_cb
- self.get_list_kwargs_cb = get_list_kwargs_cb
- self.get_pubdate_cb = get_pubdate_cb
- assert (None == enc_url == enc_length == enc_mime_type) or (enc_url is not None and enc_length is not None and enc_mime_type is not None)
- self.enc_url = enc_url
- self.enc_length = enc_length
- self.enc_mime_type = enc_mime_type
-
- def get_feed(self, param_slug=None):
- """
- Returns a utils.feedgenerator.DefaultRssFeed object, fully populated,
- representing this FeedConfiguration.
- """
- if param_slug:
- try:
- param = self.param_func(**self.param_kwargs_cb(param_slug))
- except ObjectDoesNotExist:
- raise FeedIsNotRegistered
- else:
- param = None
- current_site = sites.get_current()
- f = self._get_feed_generator_object(param)
- title_template = loader.get_template('rss/%s_title' % self.slug)
- description_template = loader.get_template('rss/%s_description' % self.slug)
- kwargs = self.get_list_kwargs.copy()
- if param and self.get_list_kwargs_cb:
- kwargs.update(self.get_list_kwargs_cb(param))
- get_list_func = self.get_list_func_cb(param)
- for obj in get_list_func(**kwargs):
- link = obj.get_absolute_url()
- if not link.startswith('http://'):
- link = u'http://%s%s' % (current_site.domain, link)
- enc = None
- if self.enc_url:
- enc_url = getattr(obj, self.enc_url)
- enc_length = getattr(obj, self.enc_length)
- enc_mime_type = getattr(obj, self.enc_mime_type)
- try:
- enc_url = enc_url()
- except TypeError:
- pass
- try:
- enc_length = enc_length()
- except TypeError:
- pass
- try:
- enc_mime_type = enc_mime_type()
- except TypeError:
- pass
- enc = feedgenerator.Enclosure(enc_url.decode('utf-8'),
- (enc_length and str(enc_length).decode('utf-8') or ''), enc_mime_type.decode('utf-8'))
- f.add_item(
- title = title_template.render(Context({'obj': obj, 'site': current_site})).decode('utf-8'),
- link = link,
- description = description_template.render(Context({'obj': obj, 'site': current_site})).decode('utf-8'),
- unique_id=link,
- enclosure=enc,
- pubdate = self.get_pubdate_cb and self.get_pubdate_cb(obj) or None,
- )
- return f
-
- def _get_feed_generator_object(self, param):
- current_site = sites.get_current()
- link = self.link_cb(param).decode()
- if not link.startswith('http://'):
- link = u'http://%s%s' % (current_site.domain, link)
- return feedgenerator.DefaultRssFeed(
- title = self.title_cb(param).decode(),
- link = link,
- description = self.description_cb(param).decode(),
- language = LANGUAGE_CODE.decode(),
- )
-
-
-# global dict used by register_feed and get_registered_feed
-_registered_feeds = {}
-
-# DEPRECATED
-class FeedIsNotRegistered(Exception):
- pass
-
-# DEPRECATED
-def register_feed(feed):
- _registered_feeds[feed.slug] = feed
-
-def register_feeds(*feeds):
- for f in feeds:
- _registered_feeds[f.slug] = f
-
-def get_registered_feed(slug):
- # try to load a RSS settings module so that feeds can be registered
- try:
- __import__(SETTINGS_MODULE + '_rss', '', '', [''])
- except (KeyError, ImportError, ValueError):
- pass
- try:
- return _registered_feeds[slug]
- except KeyError:
- raise FeedIsNotRegistered
diff --git a/django/utils/feedgenerator.py b/django/utils/feedgenerator.py
index e8e58718ff..d482685383 100644
--- a/django/utils/feedgenerator.py
+++ b/django/utils/feedgenerator.py
@@ -19,21 +19,42 @@ http://diveintomark.org/archives/2004/02/04/incompatible-rss
"""
from django.utils.xmlutils import SimplerXMLGenerator
+import datetime, re, time
+import email.Utils
+from xml.dom import minidom
+from xml.parsers.expat import ExpatError
+
+def rfc2822_date(date):
+ return email.Utils.formatdate(time.mktime(date.timetuple()))
+
+def get_tag_uri(url, date):
+ "Creates a TagURI. See http://diveintomark.org/archives/2004/05/28/howto-atom-id"
+ tag = re.sub('^http://', '', url)
+ if date is not None:
+ tag = re.sub('/', ',%s:/' % date.strftime('%Y-%m-%d'), tag, 1)
+ tag = re.sub('#', '/', tag)
+ return 'tag:' + tag
class SyndicationFeed:
"Base class for all syndication feeds. Subclasses should provide write()"
- def __init__(self, title, link, description, language=None):
- self.feed_info = {
+ def __init__(self, title, link, description, language=None, author_email=None,
+ author_name=None, author_link=None, subtitle=None, categories=None):
+ self.feed = {
'title': title,
'link': link,
'description': description,
'language': language,
+ 'author_email': author_email,
+ 'author_name': author_name,
+ 'author_link': author_link,
+ 'subtitle': subtitle,
+ 'categories': categories or (),
}
self.items = []
def add_item(self, title, link, description, author_email=None,
- author_name=None, pubdate=None, comments=None, unique_id=None,
- enclosure=None, categories=None):
+ author_name=None, pubdate=None, comments=None,
+ unique_id=None, enclosure=None, categories=()):
"""
Adds an item to the feed. All args are expected to be Python Unicode
objects except pubdate, which is a datetime.datetime object, and
@@ -49,7 +70,7 @@ class SyndicationFeed:
'comments': comments,
'unique_id': unique_id,
'enclosure': enclosure,
- 'categories': categories or [],
+ 'categories': categories or (),
})
def num_items(self):
@@ -71,6 +92,18 @@ class SyndicationFeed:
self.write(s, encoding)
return s.getvalue()
+ def latest_post_date(self):
+ """
+ Returns the latest item's pubdate. If none of them have a pubdate,
+ this returns the current date/time.
+ """
+ updates = [i['pubdate'] for i in self.items if i['pubdate'] is not None]
+ if len(updates) > 0:
+ updates.sort()
+ return updates[-1]
+ else:
+ return datetime.datetime.now()
+
class Enclosure:
"Represents an RSS enclosure"
def __init__(self, url, length, mime_type):
@@ -81,72 +114,136 @@ class RssFeed(SyndicationFeed):
def write(self, outfile, encoding):
handler = SimplerXMLGenerator(outfile, encoding)
handler.startDocument()
- self.writeRssElement(handler)
- self.writeChannelElement(handler)
- for item in self.items:
- self.writeRssItem(handler, item)
+ handler.startElement(u"rss", {u"version": self._version})
+ handler.startElement(u"channel", {})
+ handler.addQuickElement(u"title", self.feed['title'])
+ handler.addQuickElement(u"link", self.feed['link'])
+ handler.addQuickElement(u"description", self.feed['description'])
+ if self.feed['language'] is not None:
+ handler.addQuickElement(u"language", self.feed['language'])
+ self.write_items(handler)
self.endChannelElement(handler)
- self.endRssElement(handler)
-
- def writeRssElement(self, handler):
- "Adds the <rss> element to handler, taking care of versioning, etc."
- raise NotImplementedError
-
- def endRssElement(self, handler):
- "Ends the <rss> element."
handler.endElement(u"rss")
- def writeChannelElement(self, handler):
- handler.startElement(u"channel", {})
- handler.addQuickElement(u"title", self.feed_info['title'], {})
- handler.addQuickElement(u"link", self.feed_info['link'], {})
- handler.addQuickElement(u"description", self.feed_info['description'], {})
- if self.feed_info['language'] is not None:
- handler.addQuickElement(u"language", self.feed_info['language'], {})
-
def endChannelElement(self, handler):
handler.endElement(u"channel")
class RssUserland091Feed(RssFeed):
- def writeRssElement(self, handler):
- handler.startElement(u"rss", {u"version": u"0.91"})
-
- def writeRssItem(self, handler, item):
- handler.startElement(u"item", {})
- handler.addQuickElement(u"title", item['title'], {})
- handler.addQuickElement(u"link", item['link'], {})
- if item['description'] is not None:
- handler.addQuickElement(u"description", item['description'], {})
- handler.endElement(u"item")
+ _version = u"0.91"
+ def write_items(self, handler):
+ for item in self.items:
+ handler.startElement(u"item", {})
+ handler.addQuickElement(u"title", item['title'])
+ handler.addQuickElement(u"link", item['link'])
+ if item['description'] is not None:
+ handler.addQuickElement(u"description", item['description'])
+ handler.endElement(u"item")
class Rss201rev2Feed(RssFeed):
# Spec: http://blogs.law.harvard.edu/tech/rss
- def writeRssElement(self, handler):
- handler.startElement(u"rss", {u"version": u"2.0"})
+ _version = u"2.0"
+ def write_items(self, handler):
+ for item in self.items:
+ handler.startElement(u"item", {})
+ handler.addQuickElement(u"title", item['title'])
+ handler.addQuickElement(u"link", item['link'])
+ if item['description'] is not None:
+ handler.addQuickElement(u"description", item['description'])
+
+ # Author information.
+ if item['author_email'] is not None and item['author_name'] is not None:
+ handler.addQuickElement(u"author", u"%s (%s)" % \
+ (item['author_email'], item['author_name']))
+
+ if item['pubdate'] is not None:
+ handler.addQuickElement(u"pubDate", rfc2822_date(item['pubdate']).decode('ascii'))
+ if item['comments'] is not None:
+ handler.addQuickElement(u"comments", item['comments'])
+ if item['unique_id'] is not None:
+ handler.addQuickElement(u"guid", item['unique_id'])
+
+ # Enclosure.
+ if item['enclosure'] is not None:
+ handler.addQuickElement(u"enclosure", '',
+ {u"url": item['enclosure'].url, u"length": item['enclosure'].length,
+ u"type": item['enclosure'].mime_type})
+
+ # Categories.
+ for cat in item['categories']:
+ handler.addQuickElement(u"category", cat)
+
+ handler.endElement(u"item")
+
+class Atom1Feed(SyndicationFeed):
+ # Spec: http://atompub.org/2005/07/11/draft-ietf-atompub-format-10.html
+ ns = u"http://www.w3.org/2005/Atom"
+ def write(self, outfile, encoding):
+ handler = SimplerXMLGenerator(outfile, encoding)
+ handler.startDocument()
+ if self.feed['language'] is not None:
+ handler.startElement(u"feed", {u"xmlns": self.ns, u"xml:lang": self.feed['language']})
+ else:
+ handler.startElement(u"feed", {u"xmlns": self.ns})
+ handler.addQuickElement(u"title", self.feed['title'])
+ handler.addQuickElement(u"link", "", {u"href": self.feed['link']})
+ handler.addQuickElement(u"id", self.feed['link'])
+ handler.addQuickElement(u"updated", rfc2822_date(self.latest_post_date()).decode('ascii'))
+ if self.feed['author_name'] is not None:
+ handler.startElement(u"author", {})
+ handler.addQuickElement(u"name", self.feed['author_name'])
+ if self.feed['author_email'] is not None:
+ handler.addQuickElement(u"email", self.feed['author_email'])
+ if self.feed['author_link'] is not None:
+ handler.addQuickElement(u"uri", self.feed['author_link'])
+ handler.endElement(u"author")
+ if self.feed['subtitle'] is not None:
+ handler.addQuickElement(u"subtitle", self.feed['subtitle'])
+ for cat in self.feed['categories']:
+ handler.addQuickElement(u"category", "", {u"term": cat})
+ self.write_items(handler)
+ handler.endElement(u"feed")
+
+ def write_items(self, handler):
+ for item in self.items:
+ handler.startElement(u"entry", {})
+ handler.addQuickElement(u"title", item['title'])
+ handler.addQuickElement(u"link", item['link'])
+ if item['pubdate'] is not None:
+ handler.addQuickElement(u"updated", rfc2822_date(item['pubdate']).decode('ascii'))
+
+ # Author information.
+ if item['author_name'] is not None:
+ handler.startElement(u"author", {})
+ handler.addQuickElement(u"name", item['author_name'])
+ if item['author_email'] is not None:
+ handler.addQuickElement(u"email", item['author_email'])
+ handler.endElement(u"author")
+
+ # Unique ID.
+ if item['unique_id'] is not None:
+ unique_id = item['unique_id']
+ else:
+ unique_id = get_tag_uri(item['link'], item['pubdate'])
+ handler.addQuickElement(u"id", unique_id)
+
+ # Summary.
+ if item['description'] is not None:
+ handler.addQuickElement(u"summary", item['description'], {u"type": u"html"})
+
+ # Enclosure.
+ if item['enclosure'] is not None:
+ handler.addQuickElement(u"link", '',
+ {u"rel": u"enclosure",
+ u"href": item['enclosure'].url,
+ u"length": item['enclosure'].length,
+ u"type": item['enclosure'].mime_type})
+
+ # Categories:
+ for cat in item['categories']:
+ handler.addQuickElement(u"category", u"", {u"term": cat})
- def writeRssItem(self, handler, item):
- handler.startElement(u"item", {})
- handler.addQuickElement(u"title", item['title'], {})
- handler.addQuickElement(u"link", item['link'], {})
- if item['description'] is not None:
- handler.addQuickElement(u"description", item['description'], {})
- if item['author_email'] is not None and item['author_name'] is not None:
- handler.addQuickElement(u"author", u"%s (%s)" % \
- (item['author_email'], item['author_name']), {})
- if item['pubdate'] is not None:
- handler.addQuickElement(u"pubDate", item['pubdate'].strftime('%a, %d %b %Y %H:%M:%S %Z'), {})
- if item['comments'] is not None:
- handler.addQuickElement(u"comments", item['comments'], {})
- if item['unique_id'] is not None:
- handler.addQuickElement(u"guid", item['unique_id'], {})
- if item['enclosure'] is not None:
- handler.addQuickElement(u"enclosure", '',
- {u"url": item['enclosure'].url, u"length": item['enclosure'].length,
- u"type": item['enclosure'].mime_type})
- for cat in item['categories']:
- handler.addQuickElement(u"category", cat, {})
- handler.endElement(u"item")
+ handler.endElement(u"entry")
# This isolates the decision of what the system default is, so calling code can
-# do "feedgenerator.DefaultRssFeed" instead of "feedgenerator.Rss201rev2Feed".
-DefaultRssFeed = Rss201rev2Feed
+# do "feedgenerator.DefaultFeed" instead of "feedgenerator.Rss201rev2Feed".
+DefaultFeed = Rss201rev2Feed
diff --git a/django/views/rss/rss.py b/django/views/rss/rss.py
deleted file mode 100644
index 97c2f22dc4..0000000000
--- a/django/views/rss/rss.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from django.core import rss
-from django.core.exceptions import Http404
-from django.utils.httpwrappers import HttpResponse
-
-def feed(request, slug, param=None):
- try:
- f = rss.get_registered_feed(slug).get_feed(param)
- except (rss.FeedIsNotRegistered, rss.FeedDoesNotExist):
- raise Http404
- response = HttpResponse(mimetype='application/xml')
- f.write(response, 'utf-8')
- return response