mirror of https://github.com/django/django.git
Fixed #14656 -- Added Atom1Feed `published` element
Some feed aggregators make use of the `published` element as well as the `updated` element (within the Atom standard -- http://bit.ly/2YySb). The standard allows for these two elements to be present in the same entry. `Atom1Feed` had implemented the `updated` element which was incorrectly taking the date from `pubdate`.
This commit is contained in:
parent
e1c737b62f
commit
a269ea4fe0
1
AUTHORS
1
AUTHORS
|
@ -204,6 +204,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Clint Ecker
|
Clint Ecker
|
||||||
Nick Efford <nick@efford.org>
|
Nick Efford <nick@efford.org>
|
||||||
Marc Egli <frog32@me.com>
|
Marc Egli <frog32@me.com>
|
||||||
|
Matt Deacalion Stevens <matt@dirtymonkey.co.uk>
|
||||||
eibaan@gmail.com
|
eibaan@gmail.com
|
||||||
David Eklund
|
David Eklund
|
||||||
Julia Elman
|
Julia Elman
|
||||||
|
|
|
@ -43,9 +43,9 @@ class Feed(object):
|
||||||
raise Http404('Feed object does not exist.')
|
raise Http404('Feed object does not exist.')
|
||||||
feedgen = self.get_feed(obj, request)
|
feedgen = self.get_feed(obj, request)
|
||||||
response = HttpResponse(content_type=feedgen.mime_type)
|
response = HttpResponse(content_type=feedgen.mime_type)
|
||||||
if hasattr(self, 'item_pubdate'):
|
if hasattr(self, 'item_pubdate') or hasattr(self, 'item_updateddate'):
|
||||||
# if item_pubdate is defined for the feed, set header so as
|
# if item_pubdate or item_updateddate is defined for the feed, set
|
||||||
# ConditionalGetMiddleware is able to send 304 NOT MODIFIED
|
# header so as ConditionalGetMiddleware is able to send 304 NOT MODIFIED
|
||||||
response['Last-Modified'] = http_date(
|
response['Last-Modified'] = http_date(
|
||||||
timegm(feedgen.latest_post_date().utctimetuple()))
|
timegm(feedgen.latest_post_date().utctimetuple()))
|
||||||
feedgen.write(response, 'utf-8')
|
feedgen.write(response, 'utf-8')
|
||||||
|
@ -191,6 +191,11 @@ class Feed(object):
|
||||||
ltz = tzinfo.LocalTimezone(pubdate)
|
ltz = tzinfo.LocalTimezone(pubdate)
|
||||||
pubdate = pubdate.replace(tzinfo=ltz)
|
pubdate = pubdate.replace(tzinfo=ltz)
|
||||||
|
|
||||||
|
updateddate = self.__get_dynamic_attr('item_updateddate', item)
|
||||||
|
if updateddate and is_naive(updateddate):
|
||||||
|
ltz = tzinfo.LocalTimezone(updateddate)
|
||||||
|
updateddate = updateddate.replace(tzinfo=ltz)
|
||||||
|
|
||||||
feed.add_item(
|
feed.add_item(
|
||||||
title = title,
|
title = title,
|
||||||
link = link,
|
link = link,
|
||||||
|
@ -200,6 +205,7 @@ class Feed(object):
|
||||||
'item_guid_is_permalink', item),
|
'item_guid_is_permalink', item),
|
||||||
enclosure = enc,
|
enclosure = enc,
|
||||||
pubdate = pubdate,
|
pubdate = pubdate,
|
||||||
|
updateddate = updateddate,
|
||||||
author_name = author_name,
|
author_name = author_name,
|
||||||
author_email = author_email,
|
author_email = author_email,
|
||||||
author_link = author_link,
|
author_link = author_link,
|
||||||
|
|
|
@ -114,11 +114,11 @@ class SyndicationFeed(object):
|
||||||
def add_item(self, title, link, description, author_email=None,
|
def add_item(self, title, link, description, author_email=None,
|
||||||
author_name=None, author_link=None, pubdate=None, comments=None,
|
author_name=None, author_link=None, pubdate=None, comments=None,
|
||||||
unique_id=None, unique_id_is_permalink=None, enclosure=None,
|
unique_id=None, unique_id_is_permalink=None, enclosure=None,
|
||||||
categories=(), item_copyright=None, ttl=None, **kwargs):
|
categories=(), item_copyright=None, ttl=None, updateddate=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Adds an item to the feed. All args are expected to be Python Unicode
|
Adds an item to the feed. All args are expected to be Python Unicode
|
||||||
objects except pubdate, which is a datetime.datetime object, and
|
objects except pubdate and updateddate, which are datetime.datetime
|
||||||
enclosure, which is an instance of the Enclosure class.
|
objects, and enclosure, which is an instance of the Enclosure class.
|
||||||
"""
|
"""
|
||||||
to_unicode = lambda s: force_text(s, strings_only=True)
|
to_unicode = lambda s: force_text(s, strings_only=True)
|
||||||
if categories:
|
if categories:
|
||||||
|
@ -134,6 +134,7 @@ class SyndicationFeed(object):
|
||||||
'author_name': to_unicode(author_name),
|
'author_name': to_unicode(author_name),
|
||||||
'author_link': iri_to_uri(author_link),
|
'author_link': iri_to_uri(author_link),
|
||||||
'pubdate': pubdate,
|
'pubdate': pubdate,
|
||||||
|
'updateddate': updateddate,
|
||||||
'comments': to_unicode(comments),
|
'comments': to_unicode(comments),
|
||||||
'unique_id': to_unicode(unique_id),
|
'unique_id': to_unicode(unique_id),
|
||||||
'unique_id_is_permalink': unique_id_is_permalink,
|
'unique_id_is_permalink': unique_id_is_permalink,
|
||||||
|
@ -191,15 +192,20 @@ class SyndicationFeed(object):
|
||||||
|
|
||||||
def latest_post_date(self):
|
def latest_post_date(self):
|
||||||
"""
|
"""
|
||||||
Returns the latest item's pubdate. If none of them have a pubdate,
|
Returns the latest item's pubdate or updateddate. If no items
|
||||||
this returns the current date/time.
|
have either of these attributes this returns the current date/time.
|
||||||
"""
|
"""
|
||||||
updates = [i['pubdate'] for i in self.items if i['pubdate'] is not None]
|
latest_date = None
|
||||||
if len(updates) > 0:
|
date_keys = ('updateddate', 'pubdate')
|
||||||
updates.sort()
|
|
||||||
return updates[-1]
|
for item in self.items:
|
||||||
else:
|
for date_key in date_keys:
|
||||||
return datetime.datetime.now()
|
item_date = item.get(date_key)
|
||||||
|
if item_date:
|
||||||
|
if latest_date is None or item_date > latest_date:
|
||||||
|
latest_date = item_date
|
||||||
|
|
||||||
|
return latest_date or datetime.datetime.now()
|
||||||
|
|
||||||
class Enclosure(object):
|
class Enclosure(object):
|
||||||
"Represents an RSS enclosure"
|
"Represents an RSS enclosure"
|
||||||
|
@ -349,8 +355,12 @@ class Atom1Feed(SyndicationFeed):
|
||||||
def add_item_elements(self, handler, item):
|
def add_item_elements(self, handler, item):
|
||||||
handler.addQuickElement("title", item['title'])
|
handler.addQuickElement("title", item['title'])
|
||||||
handler.addQuickElement("link", "", {"href": item['link'], "rel": "alternate"})
|
handler.addQuickElement("link", "", {"href": item['link'], "rel": "alternate"})
|
||||||
|
|
||||||
if item['pubdate'] is not None:
|
if item['pubdate'] is not None:
|
||||||
handler.addQuickElement("updated", rfc3339_date(item['pubdate']))
|
handler.addQuickElement('published', rfc3339_date(item['pubdate']))
|
||||||
|
|
||||||
|
if item['updateddate'] is not None:
|
||||||
|
handler.addQuickElement('updated', rfc3339_date(item['updateddate']))
|
||||||
|
|
||||||
# Author information.
|
# Author information.
|
||||||
if item['author_name'] is not None:
|
if item['author_name'] is not None:
|
||||||
|
|
|
@ -815,6 +815,24 @@ This example illustrates all possible attributes and methods for a
|
||||||
|
|
||||||
item_pubdate = datetime.datetime(2005, 5, 3) # Hard-coded pubdate.
|
item_pubdate = datetime.datetime(2005, 5, 3) # Hard-coded pubdate.
|
||||||
|
|
||||||
|
# ITEM UPDATED -- It's optional to use one of these three. This is a
|
||||||
|
# hook that specifies how to get the updateddate for a given item.
|
||||||
|
# In each case, the method/attribute should return a Python
|
||||||
|
# datetime.datetime object.
|
||||||
|
|
||||||
|
def item_updateddate(self, item):
|
||||||
|
"""
|
||||||
|
Takes an item, as returned by items(), and returns the item's
|
||||||
|
updateddate.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def item_updateddate(self):
|
||||||
|
"""
|
||||||
|
Returns the updateddated for every item in the feed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
item_updateddate = datetime.datetime(2005, 5, 3) # Hard-coded updateddate.
|
||||||
|
|
||||||
# ITEM CATEGORIES -- It's optional to use one of these three. This is
|
# ITEM CATEGORIES -- It's optional to use one of these three. This is
|
||||||
# a hook that specifies how to get the list of categories for a given
|
# a hook that specifies how to get the list of categories for a given
|
||||||
# item. In each case, the method/attribute should return an iterable
|
# item. In each case, the method/attribute should return an iterable
|
||||||
|
@ -928,16 +946,22 @@ They share this interface:
|
||||||
* ``categories``
|
* ``categories``
|
||||||
* ``item_copyright``
|
* ``item_copyright``
|
||||||
* ``ttl``
|
* ``ttl``
|
||||||
|
* ``updateddate``
|
||||||
|
|
||||||
Extra keyword arguments will be stored for `custom feed generators`_.
|
Extra keyword arguments will be stored for `custom feed generators`_.
|
||||||
|
|
||||||
All parameters, if given, should be Unicode objects, except:
|
All parameters, if given, should be Unicode objects, except:
|
||||||
|
|
||||||
* ``pubdate`` should be a Python :class:`~datetime.datetime` object.
|
* ``pubdate`` should be a Python :class:`~datetime.datetime` object.
|
||||||
|
* ``updateddate`` should be a Python :class:`~datetime.datetime` object.
|
||||||
* ``enclosure`` should be an instance of
|
* ``enclosure`` should be an instance of
|
||||||
:class:`django.utils.feedgenerator.Enclosure`.
|
:class:`django.utils.feedgenerator.Enclosure`.
|
||||||
* ``categories`` should be a sequence of Unicode objects.
|
* ``categories`` should be a sequence of Unicode objects.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
The optional ``updateddate`` argument was added.
|
||||||
|
|
||||||
:meth:`.SyndicationFeed.write`
|
:meth:`.SyndicationFeed.write`
|
||||||
Outputs the feed in the given encoding to outfile, which is a file-like object.
|
Outputs the feed in the given encoding to outfile, which is a file-like object.
|
||||||
|
|
||||||
|
|
|
@ -342,11 +342,15 @@ SyndicationFeed
|
||||||
All parameters should be Unicode objects, except ``categories``, which
|
All parameters should be Unicode objects, except ``categories``, which
|
||||||
should be a sequence of Unicode objects.
|
should be a sequence of Unicode objects.
|
||||||
|
|
||||||
.. method:: add_item(title, link, description, [author_email=None, author_name=None, author_link=None, pubdate=None, comments=None, unique_id=None, enclosure=None, categories=(), item_copyright=None, ttl=None, **kwargs])
|
.. method:: add_item(title, link, description, [author_email=None, author_name=None, author_link=None, pubdate=None, comments=None, unique_id=None, enclosure=None, categories=(), item_copyright=None, ttl=None, updateddate=None, **kwargs])
|
||||||
|
|
||||||
Adds an item to the feed. All args are expected to be Python ``unicode``
|
Adds an item to the feed. All args are expected to be Python ``unicode``
|
||||||
objects except ``pubdate``, which is a ``datetime.datetime`` object, and
|
objects except ``pubdate`` and ``updateddate``, which are ``datetime.datetime``
|
||||||
``enclosure``, which is an instance of the ``Enclosure`` class.
|
objects, and ``enclosure``, which is an instance of the ``Enclosure`` class.
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
The optional ``updateddate`` argument was added.
|
||||||
|
|
||||||
.. method:: num_items()
|
.. method:: num_items()
|
||||||
|
|
||||||
|
@ -380,8 +384,9 @@ SyndicationFeed
|
||||||
|
|
||||||
.. method:: latest_post_date()
|
.. method:: latest_post_date()
|
||||||
|
|
||||||
Returns the latest item's ``pubdate``. If none of them have a
|
Returns the latest ``pubdate`` or ``updateddate`` for all items in the
|
||||||
``pubdate``, this returns the current date/time.
|
feed. If no items have either of these attributes this returns the
|
||||||
|
current date/time.
|
||||||
|
|
||||||
Enclosure
|
Enclosure
|
||||||
---------
|
---------
|
||||||
|
|
|
@ -67,6 +67,11 @@ Minor features
|
||||||
parameters that are passed to the ``dict`` constructor used to build the new
|
parameters that are passed to the ``dict`` constructor used to build the new
|
||||||
context level.
|
context level.
|
||||||
|
|
||||||
|
* The :class:`~django.utils.feedgenerator.Atom1Feed` syndication feed's
|
||||||
|
``updated`` element now utilizes `updateddate` instead of ``pubdate``,
|
||||||
|
allowing the ``published`` element to be included in the feed (which
|
||||||
|
relies on ``pubdate``).
|
||||||
|
|
||||||
Backwards incompatible changes in 1.7
|
Backwards incompatible changes in 1.7
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,10 @@ class TestRss2Feed(views.Feed):
|
||||||
return "Overridden description: %s" % item
|
return "Overridden description: %s" % item
|
||||||
|
|
||||||
def item_pubdate(self, item):
|
def item_pubdate(self, item):
|
||||||
return item.date
|
return item.published
|
||||||
|
|
||||||
|
def item_updateddate(self, item):
|
||||||
|
return item.updated
|
||||||
|
|
||||||
item_author_name = 'Sally Smith'
|
item_author_name = 'Sally Smith'
|
||||||
item_author_email = 'test@example.com'
|
item_author_email = 'test@example.com'
|
||||||
|
@ -72,6 +75,17 @@ class TestAtomFeed(TestRss2Feed):
|
||||||
subtitle = TestRss2Feed.description
|
subtitle = TestRss2Feed.description
|
||||||
|
|
||||||
|
|
||||||
|
class TestLatestFeed(TestRss2Feed):
|
||||||
|
"""
|
||||||
|
A feed where the latest entry date is an `updated` element.
|
||||||
|
"""
|
||||||
|
feed_type = feedgenerator.Atom1Feed
|
||||||
|
subtitle = TestRss2Feed.description
|
||||||
|
|
||||||
|
def items(self):
|
||||||
|
return Entry.objects.exclude(pk=5)
|
||||||
|
|
||||||
|
|
||||||
class ArticlesFeed(TestRss2Feed):
|
class ArticlesFeed(TestRss2Feed):
|
||||||
"""
|
"""
|
||||||
A feed to test no link being defined. Articles have no get_absolute_url()
|
A feed to test no link being defined. Articles have no get_absolute_url()
|
||||||
|
@ -115,7 +129,7 @@ class NaiveDatesFeed(TestAtomFeed):
|
||||||
A feed with naive (non-timezone-aware) dates.
|
A feed with naive (non-timezone-aware) dates.
|
||||||
"""
|
"""
|
||||||
def item_pubdate(self, item):
|
def item_pubdate(self, item):
|
||||||
return item.date
|
return item.published
|
||||||
|
|
||||||
|
|
||||||
class TZAwareDatesFeed(TestAtomFeed):
|
class TZAwareDatesFeed(TestAtomFeed):
|
||||||
|
@ -126,7 +140,7 @@ class TZAwareDatesFeed(TestAtomFeed):
|
||||||
# Provide a weird offset so that the test can know it's getting this
|
# Provide a weird offset so that the test can know it's getting this
|
||||||
# specific offset and not accidentally getting on from
|
# specific offset and not accidentally getting on from
|
||||||
# settings.TIME_ZONE.
|
# settings.TIME_ZONE.
|
||||||
return item.date.replace(tzinfo=tzinfo.FixedOffset(42))
|
return item.published.replace(tzinfo=tzinfo.FixedOffset(42))
|
||||||
|
|
||||||
|
|
||||||
class TestFeedUrlFeed(TestAtomFeed):
|
class TestFeedUrlFeed(TestAtomFeed):
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
"fields": {
|
"fields": {
|
||||||
"title": "My first entry",
|
"title": "My first entry",
|
||||||
"date": "1850-01-01 12:30:00"
|
"updated": "1850-01-01 12:30:00",
|
||||||
|
"published": "1066-09-25 20:15:00"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -12,7 +13,8 @@
|
||||||
"pk": 2,
|
"pk": 2,
|
||||||
"fields": {
|
"fields": {
|
||||||
"title": "My second entry",
|
"title": "My second entry",
|
||||||
"date": "2008-01-02 12:30:00"
|
"updated": "2008-01-02 12:30:00",
|
||||||
|
"published": "2006-03-17 18:00:00"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -20,7 +22,8 @@
|
||||||
"pk": 3,
|
"pk": 3,
|
||||||
"fields": {
|
"fields": {
|
||||||
"title": "My third entry",
|
"title": "My third entry",
|
||||||
"date": "2008-01-02 13:30:00"
|
"updated": "2008-01-02 13:30:00",
|
||||||
|
"published": "2005-06-14 10:45:00"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -28,7 +31,17 @@
|
||||||
"pk": 4,
|
"pk": 4,
|
||||||
"fields": {
|
"fields": {
|
||||||
"title": "A & B < C > D",
|
"title": "A & B < C > D",
|
||||||
"date": "2008-01-03 13:30:00"
|
"updated": "2008-01-03 13:30:00",
|
||||||
|
"published": "2005-11-25 12:11:23"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "syndication.entry",
|
||||||
|
"pk": 5,
|
||||||
|
"fields": {
|
||||||
|
"title": "My last entry",
|
||||||
|
"updated": "2013-01-20 00:00:00",
|
||||||
|
"published": "2013-03-25 20:00:00"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,10 +5,11 @@ from django.utils.encoding import python_2_unicode_compatible
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Entry(models.Model):
|
class Entry(models.Model):
|
||||||
title = models.CharField(max_length=200)
|
title = models.CharField(max_length=200)
|
||||||
date = models.DateTimeField()
|
updated = models.DateTimeField()
|
||||||
|
published = models.DateTimeField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('date',)
|
ordering = ('updated',)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
@ -24,4 +25,3 @@ class Article(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ class SyndicationFeedTest(FeedTestCase):
|
||||||
chan = chan_elem[0]
|
chan = chan_elem[0]
|
||||||
|
|
||||||
# Find the last build date
|
# Find the last build date
|
||||||
d = Entry.objects.latest('date').date
|
d = Entry.objects.latest('published').published
|
||||||
ltz = tzinfo.LocalTimezone(d)
|
ltz = tzinfo.LocalTimezone(d)
|
||||||
last_build_date = rfc2822_date(d.replace(tzinfo=ltz))
|
last_build_date = rfc2822_date(d.replace(tzinfo=ltz))
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ class SyndicationFeedTest(FeedTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Find the pubdate of the first feed item
|
# Find the pubdate of the first feed item
|
||||||
d = Entry.objects.get(pk=1).date
|
d = Entry.objects.get(pk=1).published
|
||||||
ltz = tzinfo.LocalTimezone(d)
|
ltz = tzinfo.LocalTimezone(d)
|
||||||
pub_date = rfc2822_date(d.replace(tzinfo=ltz))
|
pub_date = rfc2822_date(d.replace(tzinfo=ltz))
|
||||||
|
|
||||||
|
@ -203,10 +203,61 @@ class SyndicationFeedTest(FeedTestCase):
|
||||||
entries = feed.getElementsByTagName('entry')
|
entries = feed.getElementsByTagName('entry')
|
||||||
self.assertEqual(len(entries), Entry.objects.count())
|
self.assertEqual(len(entries), Entry.objects.count())
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
self.assertChildNodes(entry, ['title', 'link', 'id', 'summary', 'category', 'updated', 'rights', 'author'])
|
self.assertChildNodes(entry, [
|
||||||
|
'title',
|
||||||
|
'link',
|
||||||
|
'id',
|
||||||
|
'summary',
|
||||||
|
'category',
|
||||||
|
'updated',
|
||||||
|
'published',
|
||||||
|
'rights',
|
||||||
|
'author',
|
||||||
|
])
|
||||||
summary = entry.getElementsByTagName('summary')[0]
|
summary = entry.getElementsByTagName('summary')[0]
|
||||||
self.assertEqual(summary.getAttribute('type'), 'html')
|
self.assertEqual(summary.getAttribute('type'), 'html')
|
||||||
|
|
||||||
|
def test_atom_feed_published_and_updated_elements(self):
|
||||||
|
"""
|
||||||
|
Test that the published and updated elements are not
|
||||||
|
the same and now adhere to RFC 4287.
|
||||||
|
"""
|
||||||
|
response = self.client.get('/syndication/atom/')
|
||||||
|
feed = minidom.parseString(response.content).firstChild
|
||||||
|
entries = feed.getElementsByTagName('entry')
|
||||||
|
|
||||||
|
published = entries[0].getElementsByTagName('published')[0].firstChild.wholeText
|
||||||
|
updated = entries[0].getElementsByTagName('updated')[0].firstChild.wholeText
|
||||||
|
|
||||||
|
self.assertNotEqual(published, updated)
|
||||||
|
|
||||||
|
def test_latest_post_date(self):
|
||||||
|
"""
|
||||||
|
Test that both the published and updated dates are
|
||||||
|
considered when determining the latest post date.
|
||||||
|
"""
|
||||||
|
# this feed has a `published` element with the latest date
|
||||||
|
response = self.client.get('/syndication/atom/')
|
||||||
|
feed = minidom.parseString(response.content).firstChild
|
||||||
|
updated = feed.getElementsByTagName('updated')[0].firstChild.wholeText
|
||||||
|
|
||||||
|
d = Entry.objects.latest('published').published
|
||||||
|
ltz = tzinfo.LocalTimezone(d)
|
||||||
|
latest_published = rfc3339_date(d.replace(tzinfo=ltz))
|
||||||
|
|
||||||
|
self.assertEqual(updated, latest_published)
|
||||||
|
|
||||||
|
# this feed has an `updated` element with the latest date
|
||||||
|
response = self.client.get('/syndication/latest/')
|
||||||
|
feed = minidom.parseString(response.content).firstChild
|
||||||
|
updated = feed.getElementsByTagName('updated')[0].firstChild.wholeText
|
||||||
|
|
||||||
|
d = Entry.objects.exclude(pk=5).latest('updated').updated
|
||||||
|
ltz = tzinfo.LocalTimezone(d)
|
||||||
|
latest_updated = rfc3339_date(d.replace(tzinfo=ltz))
|
||||||
|
|
||||||
|
self.assertEqual(updated, latest_updated)
|
||||||
|
|
||||||
def test_custom_feed_generator(self):
|
def test_custom_feed_generator(self):
|
||||||
response = self.client.get('/syndication/custom/')
|
response = self.client.get('/syndication/custom/')
|
||||||
feed = minidom.parseString(response.content).firstChild
|
feed = minidom.parseString(response.content).firstChild
|
||||||
|
@ -219,7 +270,18 @@ class SyndicationFeedTest(FeedTestCase):
|
||||||
self.assertEqual(len(entries), Entry.objects.count())
|
self.assertEqual(len(entries), Entry.objects.count())
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
self.assertEqual(entry.getAttribute('bacon'), 'yum')
|
self.assertEqual(entry.getAttribute('bacon'), 'yum')
|
||||||
self.assertChildNodes(entry, ['title', 'link', 'id', 'summary', 'ministry', 'rights', 'author', 'updated', 'category'])
|
self.assertChildNodes(entry, [
|
||||||
|
'title',
|
||||||
|
'link',
|
||||||
|
'id',
|
||||||
|
'summary',
|
||||||
|
'ministry',
|
||||||
|
'rights',
|
||||||
|
'author',
|
||||||
|
'updated',
|
||||||
|
'published',
|
||||||
|
'category',
|
||||||
|
])
|
||||||
summary = entry.getElementsByTagName('summary')[0]
|
summary = entry.getElementsByTagName('summary')[0]
|
||||||
self.assertEqual(summary.getAttribute('type'), 'html')
|
self.assertEqual(summary.getAttribute('type'), 'html')
|
||||||
|
|
||||||
|
@ -245,7 +307,7 @@ class SyndicationFeedTest(FeedTestCase):
|
||||||
doc = minidom.parseString(response.content)
|
doc = minidom.parseString(response.content)
|
||||||
updated = doc.getElementsByTagName('updated')[0].firstChild.wholeText
|
updated = doc.getElementsByTagName('updated')[0].firstChild.wholeText
|
||||||
|
|
||||||
d = Entry.objects.latest('date').date
|
d = Entry.objects.latest('published').published
|
||||||
ltz = tzinfo.LocalTimezone(d)
|
ltz = tzinfo.LocalTimezone(d)
|
||||||
latest = rfc3339_date(d.replace(tzinfo=ltz))
|
latest = rfc3339_date(d.replace(tzinfo=ltz))
|
||||||
|
|
||||||
|
@ -257,12 +319,12 @@ class SyndicationFeedTest(FeedTestCase):
|
||||||
"""
|
"""
|
||||||
response = self.client.get('/syndication/aware-dates/')
|
response = self.client.get('/syndication/aware-dates/')
|
||||||
doc = minidom.parseString(response.content)
|
doc = minidom.parseString(response.content)
|
||||||
updated = doc.getElementsByTagName('updated')[0].firstChild.wholeText
|
published = doc.getElementsByTagName('published')[0].firstChild.wholeText
|
||||||
self.assertEqual(updated[-6:], '+00:42')
|
self.assertEqual(published[-6:], '+00:42')
|
||||||
|
|
||||||
def test_feed_last_modified_time(self):
|
def test_feed_last_modified_time(self):
|
||||||
response = self.client.get('/syndication/naive-dates/')
|
response = self.client.get('/syndication/naive-dates/')
|
||||||
self.assertEqual(response['Last-Modified'], 'Thu, 03 Jan 2008 19:30:00 GMT')
|
self.assertEqual(response['Last-Modified'], 'Tue, 26 Mar 2013 01:00:00 GMT')
|
||||||
|
|
||||||
# No last-modified when feed has no item_pubdate
|
# No last-modified when feed has no item_pubdate
|
||||||
response = self.client.get('/syndication/no_pubdate/')
|
response = self.client.get('/syndication/no_pubdate/')
|
||||||
|
|
|
@ -15,6 +15,7 @@ urlpatterns = patterns('django.contrib.syndication.views',
|
||||||
(r'^syndication/rss091/$', feeds.TestRss091Feed()),
|
(r'^syndication/rss091/$', feeds.TestRss091Feed()),
|
||||||
(r'^syndication/no_pubdate/$', feeds.TestNoPubdateFeed()),
|
(r'^syndication/no_pubdate/$', feeds.TestNoPubdateFeed()),
|
||||||
(r'^syndication/atom/$', feeds.TestAtomFeed()),
|
(r'^syndication/atom/$', feeds.TestAtomFeed()),
|
||||||
|
(r'^syndication/latest/$', feeds.TestLatestFeed()),
|
||||||
(r'^syndication/custom/$', feeds.TestCustomFeed()),
|
(r'^syndication/custom/$', feeds.TestCustomFeed()),
|
||||||
(r'^syndication/naive-dates/$', feeds.NaiveDatesFeed()),
|
(r'^syndication/naive-dates/$', feeds.NaiveDatesFeed()),
|
||||||
(r'^syndication/aware-dates/$', feeds.TZAwareDatesFeed()),
|
(r'^syndication/aware-dates/$', feeds.TZAwareDatesFeed()),
|
||||||
|
|
Loading…
Reference in New Issue