Django

Code

Changeset 8311

Show
Ignore:
Timestamp:
08/11/08 17:22:26 (11 months ago)
Author:
jacob
Message:

Added a number of callbacks to SyndicationFeed? for adding custom attributes and elements to feeds. Refs #6547.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/utils/feedgenerator.py

    r8216 r8311  
    2020""" 
    2121 
     22import re 
     23import datetime 
    2224from django.utils.xmlutils import SimplerXMLGenerator 
    2325from django.utils.encoding import force_unicode, iri_to_uri 
    24 import datetime, re, time 
    2526 
    2627def rfc2822_date(date): 
     
    5758    def __init__(self, title, link, description, language=None, author_email=None, 
    5859            author_name=None, author_link=None, subtitle=None, categories=None, 
    59             feed_url=None, feed_copyright=None, feed_guid=None, ttl=None): 
     60            feed_url=None, feed_copyright=None, feed_guid=None, ttl=None, **kwargs): 
    6061        to_unicode = lambda s: force_unicode(s, strings_only=True) 
    6162        if categories: 
     
    7677            'ttl': ttl, 
    7778        } 
     79        self.feed.update(kwargs) 
    7880        self.items = [] 
    7981 
    8082    def add_item(self, title, link, description, author_email=None, 
    8183        author_name=None, author_link=None, pubdate=None, comments=None, 
    82         unique_id=None, enclosure=None, categories=(), item_copyright=None, ttl=None): 
     84        unique_id=None, enclosure=None, categories=(), item_copyright=None,  
     85        ttl=None, **kwargs): 
    8386        """ 
    8487        Adds an item to the feed. All args are expected to be Python Unicode 
     
    8992        if categories: 
    9093            categories = [to_unicode(c) for c in categories] 
    91         self.items.append(
     94        item =
    9295            'title': to_unicode(title), 
    9396            'link': iri_to_uri(link), 
     
    103106            'item_copyright': to_unicode(item_copyright), 
    104107            'ttl': ttl, 
    105         }) 
     108        } 
     109        item.update(kwargs) 
     110        self.items.append(item) 
    106111 
    107112    def num_items(self): 
    108113        return len(self.items) 
    109114 
     115    def root_attributes(self): 
     116        """ 
     117        Return extra attributes to place on the root (i.e. feed/channel) element. 
     118        Called from write(). 
     119        """ 
     120        return {} 
     121         
     122    def add_root_elements(self, handler): 
     123        """ 
     124        Add elements in the the root (i.e. feed/channel) element. Called  
     125        from write(). 
     126        """ 
     127        pass 
     128         
     129    def item_attributes(self, item): 
     130        """ 
     131        Return extra attributes to place on each item (i.e. item/entry) element. 
     132        """ 
     133        return {} 
     134         
     135    def add_item_elements(self, handler, item): 
     136        """ 
     137        Add elements on each item (i.e. item/entry) element. 
     138        """ 
     139        pass 
     140         
    110141    def write(self, outfile, encoding): 
    111142        """ 
     
    149180        handler.startDocument() 
    150181        handler.startElement(u"rss", {u"version": self._version}) 
    151         handler.startElement(u"channel", {}) 
     182        handler.startElement(u"channel", self.root_attributes()) 
     183        self.add_root_elements(handler) 
     184        self.write_items(handler) 
     185        self.endChannelElement(handler) 
     186        handler.endElement(u"rss") 
     187 
     188    def write_items(self, handler): 
     189        for item in self.items: 
     190            handler.startElement(u'item', self.item_attributes(item)) 
     191            self.add_item_elements(handler, item) 
     192            handler.endElement(u"item") 
     193 
     194    def add_root_elements(self, handler): 
    152195        handler.addQuickElement(u"title", self.feed['title']) 
    153196        handler.addQuickElement(u"link", self.feed['link']) 
     
    162205        if self.feed['ttl'] is not None: 
    163206            handler.addQuickElement(u"ttl", self.feed['ttl']) 
    164         self.write_items(handler) 
    165         self.endChannelElement(handler) 
    166         handler.endElement(u"rss") 
    167207 
    168208    def endChannelElement(self, handler): 
     
    171211class RssUserland091Feed(RssFeed): 
    172212    _version = u"0.91" 
    173     def write_items(self, handler): 
    174         for item in self.items: 
    175             handler.startElement(u"item", {}) 
    176             handler.addQuickElement(u"title", item['title']) 
    177             handler.addQuickElement(u"link", item['link']) 
    178             if item['description'] is not None: 
    179                 handler.addQuickElement(u"description", item['description']) 
    180             handler.endElement(u"item") 
     213    def add_item_elements(self, handler, item): 
     214        handler.addQuickElement(u"title", item['title']) 
     215        handler.addQuickElement(u"link", item['link']) 
     216        if item['description'] is not None: 
     217            handler.addQuickElement(u"description", item['description']) 
    181218 
    182219class Rss201rev2Feed(RssFeed): 
    183220    # Spec: http://blogs.law.harvard.edu/tech/rss 
    184221    _version = u"2.0" 
    185     def write_items(self, handler): 
    186         for item in self.items: 
    187             handler.startElement(u"item", {}) 
    188             handler.addQuickElement(u"title", item['title']) 
    189             handler.addQuickElement(u"link", item['link']) 
    190             if item['description'] is not None: 
    191                 handler.addQuickElement(u"description", item['description']) 
    192  
    193             # Author information. 
    194             if item["author_name"] and item["author_email"]: 
    195                 handler.addQuickElement(u"author", "%s (%s)" % \ 
    196                     (item['author_email'], item['author_name'])) 
    197             elif item["author_email"]: 
    198                 handler.addQuickElement(u"author", item["author_email"]) 
    199             elif item["author_name"]: 
    200                 handler.addQuickElement(u"dc:creator", item["author_name"], {"xmlns:dc": u"http://purl.org/dc/elements/1.1/"}) 
    201  
    202             if item['pubdate'] is not None: 
    203                 handler.addQuickElement(u"pubDate", rfc2822_date(item['pubdate']).decode('ascii')) 
    204             if item['comments'] is not None: 
    205                 handler.addQuickElement(u"comments", item['comments']) 
    206             if item['unique_id'] is not None: 
    207                 handler.addQuickElement(u"guid", item['unique_id']) 
    208             if item['ttl'] is not None: 
    209                 handler.addQuickElement(u"ttl", item['ttl']) 
    210  
    211             # Enclosure. 
    212             if item['enclosure'] is not None: 
    213                 handler.addQuickElement(u"enclosure", '', 
    214                     {u"url": item['enclosure'].url, u"length": item['enclosure'].length, 
    215                         u"type": item['enclosure'].mime_type}) 
    216  
    217             # Categories. 
    218             for cat in item['categories']: 
    219                 handler.addQuickElement(u"category", cat) 
    220  
    221             handler.endElement(u"item") 
     222    def add_item_elements(self, handler, item): 
     223        handler.addQuickElement(u"title", item['title']) 
     224        handler.addQuickElement(u"link", item['link']) 
     225        if item['description'] is not None: 
     226            handler.addQuickElement(u"description", item['description']) 
     227 
     228        # Author information. 
     229        if item["author_name"] and item["author_email"]: 
     230            handler.addQuickElement(u"author", "%s (%s)" % \ 
     231                (item['author_email'], item['author_name'])) 
     232        elif item["author_email"]: 
     233            handler.addQuickElement(u"author", item["author_email"]) 
     234        elif item["author_name"]: 
     235            handler.addQuickElement(u"dc:creator", item["author_name"], {"xmlns:dc": u"http://purl.org/dc/elements/1.1/"}) 
     236 
     237        if item['pubdate'] is not None: 
     238            handler.addQuickElement(u"pubDate", rfc2822_date(item['pubdate']).decode('ascii')) 
     239        if item['comments'] is not None: 
     240            handler.addQuickElement(u"comments", item['comments']) 
     241        if item['unique_id'] is not None: 
     242            handler.addQuickElement(u"guid", item['unique_id']) 
     243        if item['ttl'] is not None: 
     244            handler.addQuickElement(u"ttl", item['ttl']) 
     245 
     246        # Enclosure. 
     247        if item['enclosure'] is not None: 
     248            handler.addQuickElement(u"enclosure", '', 
     249                {u"url": item['enclosure'].url, u"length": item['enclosure'].length, 
     250                    u"type": item['enclosure'].mime_type}) 
     251 
     252        # Categories. 
     253        for cat in item['categories']: 
     254            handler.addQuickElement(u"category", cat) 
    222255 
    223256class Atom1Feed(SyndicationFeed): 
     
    225258    mime_type = 'application/atom+xml' 
    226259    ns = u"http://www.w3.org/2005/Atom" 
     260 
    227261    def write(self, outfile, encoding): 
    228262        handler = SimplerXMLGenerator(outfile, encoding) 
    229263        handler.startDocument() 
     264        handler.startElement(u'feed', self.root_attributes()) 
     265        self.add_root_elements(handler) 
     266        self.write_items(handler) 
     267        handler.endElement(u"feed") 
     268 
     269    def root_element_attributes(self): 
    230270        if self.feed['language'] is not None: 
    231             handler.startElement(u"feed", {u"xmlns": self.ns, u"xml:lang": self.feed['language']}) 
     271            return {u"xmlns": self.ns, u"xml:lang": self.feed['language']} 
    232272        else: 
    233             handler.startElement(u"feed", {u"xmlns": self.ns}) 
     273            return {u"xmlns": self.ns} 
     274 
     275    def add_root_elements(self, handler): 
    234276        handler.addQuickElement(u"title", self.feed['title']) 
    235277        handler.addQuickElement(u"link", "", {u"rel": u"alternate", u"href": self.feed['link']}) 
     
    252294        if self.feed['feed_copyright'] is not None: 
    253295            handler.addQuickElement(u"rights", self.feed['feed_copyright']) 
    254         self.write_items(handler) 
    255         handler.endElement(u"feed") 
    256  
     296         
    257297    def write_items(self, handler): 
    258298        for item in self.items: 
    259             handler.startElement(u"entry", {}) 
    260             handler.addQuickElement(u"title", item['title']) 
    261             handler.addQuickElement(u"link", u"", {u"href": item['link'], u"rel": u"alternate"}) 
    262             if item['pubdate'] is not None: 
    263                 handler.addQuickElement(u"updated", rfc3339_date(item['pubdate']).decode('ascii')) 
    264  
    265             # Author information. 
    266             if item['author_name'] is not None: 
    267                 handler.startElement(u"author", {}) 
    268                 handler.addQuickElement(u"name", item['author_name']) 
    269                 if item['author_email'] is not None: 
    270                     handler.addQuickElement(u"email", item['author_email']) 
    271                 if item['author_link'] is not None: 
    272                     handler.addQuickElement(u"uri", item['author_link']) 
    273                 handler.endElement(u"author") 
    274  
    275             # Unique ID. 
    276             if item['unique_id'] is not None: 
    277                 unique_id = item['unique_id'] 
    278             else: 
    279                 unique_id = get_tag_uri(item['link'], item['pubdate']) 
    280             handler.addQuickElement(u"id", unique_id) 
    281  
    282             # Summary. 
    283             if item['description'] is not None: 
    284                 handler.addQuickElement(u"summary", item['description'], {u"type": u"html"}) 
    285  
    286             # Enclosure. 
    287             if item['enclosure'] is not None: 
    288                 handler.addQuickElement(u"link", '', 
    289                     {u"rel": u"enclosure", 
    290                      u"href": item['enclosure'].url, 
    291                      u"length": item['enclosure'].length, 
    292                      u"type": item['enclosure'].mime_type}) 
    293  
    294             # Categories. 
    295             for cat in item['categories']: 
    296                 handler.addQuickElement(u"category", u"", {u"term": cat}) 
    297  
    298             # Rights. 
    299             if item['item_copyright'] is not None: 
    300                 handler.addQuickElement(u"rights", item['item_copyright']) 
    301  
     299            handler.startElement(u"entry", self.item_attributes(item)) 
     300            self.add_item_elements(handler, item) 
    302301            handler.endElement(u"entry") 
     302             
     303    def add_item_elements(self, handler, item): 
     304        handler.addQuickElement(u"title", item['title']) 
     305        handler.addQuickElement(u"link", u"", {u"href": item['link'], u"rel": u"alternate"}) 
     306        if item['pubdate'] is not None: 
     307            handler.addQuickElement(u"updated", rfc3339_date(item['pubdate']).decode('ascii')) 
     308 
     309        # Author information. 
     310        if item['author_name'] is not None: 
     311            handler.startElement(u"author", {}) 
     312            handler.addQuickElement(u"name", item['author_name']) 
     313            if item['author_email'] is not None: 
     314                handler.addQuickElement(u"email", item['author_email']) 
     315            if item['author_link'] is not None: 
     316                handler.addQuickElement(u"uri", item['author_link']) 
     317            handler.endElement(u"author") 
     318 
     319        # Unique ID. 
     320        if item['unique_id'] is not None: 
     321            unique_id = item['unique_id'] 
     322        else: 
     323            unique_id = get_tag_uri(item['link'], item['pubdate']) 
     324        handler.addQuickElement(u"id", unique_id) 
     325 
     326        # Summary. 
     327        if item['description'] is not None: 
     328            handler.addQuickElement(u"summary", item['description'], {u"type": u"html"}) 
     329 
     330        # Enclosure. 
     331        if item['enclosure'] is not None: 
     332            handler.addQuickElement(u"link", '', 
     333                {u"rel": u"enclosure", 
     334                 u"href": item['enclosure'].url, 
     335                 u"length": item['enclosure'].length, 
     336                 u"type": item['enclosure'].mime_type}) 
     337 
     338        # Categories. 
     339        for cat in item['categories']: 
     340            handler.addQuickElement(u"category", u"", {u"term": cat}) 
     341 
     342        # Rights. 
     343        if item['item_copyright'] is not None: 
     344            handler.addQuickElement(u"rights", item['item_copyright']) 
    303345 
    304346# This isolates the decision of what the system default is, so calling code can 
  • django/trunk/docs/syndication_feeds.txt

    r7361 r8311  
    802802`django/utils/feedgenerator.py`_. 
    803803 
    804 Feel free to use this framework on your own, for lower-level tasks. 
     804You use this framework on your own, for lower-level feed generation. You can 
     805also create custom feed generator subclasses for use with the ``feed_type`` 
     806``Feed`` option. 
     807 
     808``SyndicationFeed`` classes 
     809--------------------------- 
    805810 
    806811The ``feedgenerator`` module contains a base class ``SyndicationFeed`` and 
     
    814819They share this interface: 
    815820 
    816 ``__init__(title, link, description, language=None, author_email=None,`` 
    817 ``author_name=None, author_link=None, subtitle=None, categories=None,`` 
    818 ``feed_url=None)`` 
    819  
    820 Initializes the feed with the given metadata, which applies to the entire feed 
    821 (i.e., not just to a specific item in the feed). 
    822  
    823 All parameters, if given, should be Unicode objects, except ``categories``, 
    824 which should be a sequence of Unicode objects. 
    825  
    826 ``add_item(title, link, description, author_email=None, author_name=None,`` 
    827 ``pubdate=None, comments=None, unique_id=None, enclosure=None, categories=())`` 
    828  
    829 Add an item to the feed with the given parameters. All parameters, if given, 
    830 should be Unicode objects, except: 
    831  
    832     * ``pubdate`` should be a `Python datetime object`_. 
    833     * ``enclosure`` should be an instance of ``feedgenerator.Enclosure``. 
    834     * ``categories`` should be a sequence of Unicode objects. 
    835  
    836 ``write(outfile, encoding)`` 
    837  
    838 Outputs the feed in the given encoding to outfile, which is a file-like object. 
    839  
    840 ``writeString(encoding)`` 
    841  
    842 Returns the feed as a string in the given encoding. 
    843  
    844 Example usage 
    845 ------------- 
    846  
    847 This example creates an Atom 1.0 feed and prints it to standard output:: 
     821``SyndicationFeed.__init__(**kwargs)`` 
     822    Initialize the feed with the given dictionary of metadata, which applies to 
     823    the entire feed. Required keyword arguments are: 
     824     
     825        * ``title`` 
     826        * ``link`` 
     827        * ``description`` 
     828         
     829    There's also a bunch of other optional keywords: 
     830     
     831        * ``language`` 
     832        * ``author_email`` 
     833        * ``author_name`` 
     834        * ``author_link`` 
     835        * ``subtitle`` 
     836        * ``categories`` 
     837        * ``feed_url`` 
     838        * ``feed_copyright`` 
     839        * ``feed_guid`` 
     840        * ``ttl`` 
     841         
     842    Any extra keyword arguments you pass to ``__init__`` will be stored in 
     843    ``self.feed`` for use with `custom feed generators`_. 
     844 
     845    All parameters should be Unicode objects, except ``categories``, which 
     846    should be a sequence of Unicode objects. 
     847 
     848``SyndicationFeed.add_item(**kwargs)`` 
     849    Add an item to the feed with the given parameters.  
     850 
     851    Required keyword arguments are: 
     852     
     853        * ``title`` 
     854        * ``link`` 
     855        * ``description`` 
     856 
     857    Optional keyword arguments are: 
     858 
     859        * ``author_email`` 
     860        * ``author_name`` 
     861        * ``author_link`` 
     862        * ``pubdate`` 
     863        * ``comments`` 
     864        * ``unique_id`` 
     865        * ``enclosure`` 
     866        * ``categories`` 
     867        * ``item_copyright`` 
     868        * ``ttl`` 
     869 
     870    Extra keyword arguments will be stored for `custom feed generators`_. 
     871 
     872    All parameters, if given, should be Unicode objects, except: 
     873 
     874        * ``pubdate`` should be a `Python datetime object`_. 
     875        * ``enclosure`` should be an instance of ``feedgenerator.Enclosure``. 
     876        * ``categories`` should be a sequence of Unicode objects. 
     877         
     878``SyndicationFeed.write(outfile, encoding)`` 
     879    Outputs the feed in the given ``encoding`` to ``outfile``, which must be a 
     880    file-like object. 
     881 
     882``SyndicationFeed.writeString(encoding)`` 
     883    Returns the feed as a string in the given ``encoding``. 
     884 
     885For example, to create an Atom 1.0 feed and print it to standard output:: 
    848886 
    849887    >>> from django.utils import feedgenerator 
     
    858896    >>> print f.writeString('utf8') 
    859897    <?xml version="1.0" encoding="utf8"?> 
    860     <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title>My Weblog</title> 
    861     <link href="http://www.example.com/"></link><id>http://www.example.com/</id> 
    862     <updated>Sat, 12 Nov 2005 00:28:43 -0000</updated><entry><title>Hot dog today</title> 
    863     <link>http://www.example.com/entries/1/</link><id>tag:www.example.com/entries/1/</id> 
    864     <summary type="html">&lt;p&gt;Today I had a Vienna Beef hot dog. It was pink, plump and perfect.&lt;/p&gt;</summary> 
    865     </entry></feed> 
     898    <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"> 
     899    ... 
     900    </feed> 
    866901 
    867902.. _django/utils/feedgenerator.py: http://code.djangoproject.com/browser/django/trunk/django/utils/feedgenerator.py 
    868903.. _Python datetime object: http://www.python.org/doc/current/lib/module-datetime.html 
     904 
     905Custom feed generators 
     906---------------------- 
     907 
     908If you need to produce a custom feed format, you've got a couple of options. 
     909     
     910If the feed format is totally custom, you'll want to subclass 
     911``SyndicationFeed`` and completely replace the ``write()`` and 
     912``writeString()`` methods. 
     913 
     914However, if the feed format is a spin-off of RSS or Atom (i.e. GeoRSS_, Apple's 
     915`iTunes podcast format`_, etc.), you've got a better choice. These types of 
     916feeds typically add extra elements and/or attributes to the underlying format, 
     917and there are a set of methods that ``SyndicationFeed`` calls to get these extra 
     918attributes. Thus, you can subclass the appropriate feed generator class 
     919(``Atom1Feed`` or ``Rss201rev2Feed``) and extend these callbacks. They are: 
     920       
     921.. _georss: http://georss.org/ 
     922.. _itunes podcast format: http://www.apple.com/itunes/store/podcaststechspecs.html 
     923 
     924``SyndicationFeed.root_attributes(self, )`` 
     925    Return a ``dict`` of attributes to add to the root feed element 
     926    (``feed``/``channel``). 
     927     
     928``SyndicationFeed.add_root_elements(self, handler)`` 
     929    Callback to add elements inside the root feed element 
     930    (``feed``/``channel``). ``handler`` is an `XMLGenerator`_ from Python's 
     931    built-in SAX library; you'll call methods on it to add to the XML 
     932    document in process. 
     933     
     934``SyndicationFeed.item_attributes(self, item)`` 
     935    Return a ``dict`` of attributes to add to each item (``item``/``entry``) 
     936    element. The argument, ``item``, is a dictionary of all the data passed to 
     937    ``SyndicationFeed.add_item()``. 
     938     
     939``SyndicationFeed.add_item_elements(self, handler, item)`` 
     940    Callback to add elements to each item (``item``/``entry``) element. 
     941    ``handler`` and ``item`` are as above. 
     942 
     943.. warning:: 
     944 
     945    If you override any of these methods, be sure to call the superclass methods 
     946    since they add the required elements for each feed format. 
     947 
     948For example, you might start implementing an iTunes RSS feed generator like so:: 
     949 
     950    class iTunesFeed(Rss201rev2Feed): 
     951        def root_attibutes(self): 
     952            attrs = super(iTunesFeed, self).root_attibutes() 
     953            attrs['xmlns:itunes'] = 'http://www.itunes.com/dtds/podcast-1.0.dtd 
     954            return attrs 
     955             
     956        def add_root_elements(self, handler): 
     957            super(iTunesFeed, self).add_root_elements(handler) 
     958            handler.addQuickElement('itunes:explicit', 'clean') 
     959 
     960Obviously there's a lot more work to be done for a complete custom feed class, 
     961but the above example should demonstrate the basic idea. 
     962 
     963.. _XMLGenerator: http://docs.python.org/dev/library/xml.sax.utils.html#xml.sax.saxutils.XMLGenerator 
  • django/trunk/tests/regressiontests/syndication/feeds.py

    r8310 r8311  
    2222class TestAtomFeed(TestRssFeed): 
    2323    feed_type = Atom1Feed 
     24 
     25class MyCustomAtom1Feed(Atom1Feed): 
     26    """ 
     27    Test of a custom feed generator class. 
     28    """     
     29    def root_attributes(self): 
     30        attrs = super(MyCustomAtom1Feed, self).root_attributes() 
     31        attrs[u'django'] = u'rocks' 
     32        return attrs 
     33         
     34    def add_root_elements(self, handler): 
     35        super(MyCustomAtom1Feed, self).add_root_elements(handler) 
     36        handler.addQuickElement(u'spam', u'eggs') 
     37         
     38    def item_attributes(self, item): 
     39        attrs = super(MyCustomAtom1Feed, self).item_attributes(item) 
     40        attrs[u'bacon'] = u'yum' 
     41        return attrs 
     42         
     43    def add_item_elements(self, handler, item): 
     44        super(MyCustomAtom1Feed, self).add_item_elements(handler, item) 
     45        handler.addQuickElement(u'ministry', u'silly walks') 
     46     
     47class TestCustomFeed(TestAtomFeed): 
     48    feed_type = MyCustomAtom1Feed 
  • django/trunk/tests/regressiontests/syndication/tests.py

    r8310 r8311  
    55from django.test.client import Client 
    66from models import Entry 
     7try: 
     8    set 
     9except NameError: 
     10    from sets import Set as set 
    711 
    812class SyndicationFeedTest(TestCase): 
    913    fixtures = ['feeddata.json'] 
     14 
     15    def assertChildNodes(self, elem, expected): 
     16        actual = set([n.nodeName for n in elem.childNodes]) 
     17        expected = set(expected) 
     18        self.assertEqual(actual, expected) 
    1019 
    1120    def test_rss_feed(self): 
     
    1322        doc = minidom.parseString(response.content) 
    1423        self.assertEqual(len(doc.getElementsByTagName('channel')), 1) 
    15         self.assertEqual(len(doc.getElementsByTagName('item')), Entry.objects.count()) 
     24 
     25        chan = doc.getElementsByTagName('channel')[0] 
     26        self.assertChildNodes(chan, ['title', 'link', 'description', 'language', 'lastBuildDate', 'item']) 
     27     
     28        items = chan.getElementsByTagName('item') 
     29        self.assertEqual(len(items), Entry.objects.count()) 
     30        for item in items: 
     31            self.assertChildNodes(item, ['title', 'link', 'description', 'guid']) 
    1632     
    1733    def test_atom_feed(self): 
    1834        response = self.client.get('/syndication/feeds/atom/') 
    1935        doc = minidom.parseString(response.content) 
    20         self.assertEqual(len(doc.getElementsByTagName('feed')), 1) 
    21         self.assertEqual(len(doc.getElementsByTagName('entry')), Entry.objects.count()) 
     36         
     37        feed = doc.firstChild 
     38        self.assertEqual(feed.nodeName, 'feed') 
     39        self.assertChildNodes(feed, ['title', 'link', 'id', 'updated', 'entry'])         
     40         
     41        entries = feed.getElementsByTagName('entry') 
     42        self.assertEqual(len(entries), Entry.objects.count()) 
     43        for entry in entries: 
     44            self.assertChildNodes(entry, ['title', 'link', 'id', 'summary']) 
     45            summary = entry.getElementsByTagName('summary')[0] 
     46            self.assertEqual(summary.getAttribute('type'), 'html') 
    2247     
     48    def test_custom_feed_generator(self): 
     49        response = self.client.get('/syndication/feeds/custom/') 
     50        doc = minidom.parseString(response.content) 
     51         
     52        feed = doc.firstChild 
     53        self.assertEqual(feed.nodeName, 'feed') 
     54        self.assertEqual(feed.getAttribute('django'), 'rocks') 
     55        self.assertChildNodes(feed, ['title', 'link', 'id', 'updated', 'entry', 'spam'])         
     56         
     57        entries = feed.getElementsByTagName('entry') 
     58        self.assertEqual(len(entries), Entry.objects.count()) 
     59        for entry in entries: 
     60            self.assertEqual(entry.getAttribute('bacon'), 'yum') 
     61            self.assertChildNodes(entry, ['title', 'link', 'id', 'summary', 'ministry']) 
     62            summary = entry.getElementsByTagName('summary')[0] 
     63            self.assertEqual(summary.getAttribute('type'), 'html') 
     64         
    2365    def test_complex_base_url(self): 
    2466        """ 
  • django/trunk/tests/regressiontests/syndication/urls.py

    r8310 r8311  
    1 from feeds import TestRssFeed, TestAtomFeed, ComplexFeed 
     1from feeds import TestRssFeed, TestAtomFeed, TestCustomFeed, ComplexFeed 
    22from django.conf.urls.defaults import patterns 
    33 
     
    66    'rss': TestRssFeed, 
    77    'atom': TestAtomFeed, 
     8    'custom': TestCustomFeed, 
    89     
    910}