PageRenderTime 27ms CodeModel.GetById 15ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

/docs/ref/contrib/syndication.txt

https://code.google.com/p/mango-py/
Plain Text | 949 lines | 707 code | 242 blank | 0 comment | 0 complexity | 7dbcf30ca067e5be711dfbbfbcb3c24a MD5 | raw file
  1==============================
  2The syndication feed framework
  3==============================
  4
  5.. module:: django.contrib.syndication
  6   :synopsis: A framework for generating syndication feeds, in RSS and Atom,
  7              quite easily.
  8
  9Django comes with a high-level syndication-feed-generating framework
 10that makes creating RSS_ and Atom_ feeds easy.
 11
 12To create any syndication feed, all you have to do is write a short
 13Python class. You can create as many feeds as you want.
 14
 15Django also comes with a lower-level feed-generating API. Use this if
 16you want to generate feeds outside of a Web context, or in some other
 17lower-level way.
 18
 19.. _RSS: http://www.whatisrss.com/
 20.. _Atom: http://www.atomenabled.org/
 21
 22The high-level framework
 23========================
 24
 25.. versionchanged:: 1.2
 26   The high-level feeds framework was refactored in Django 1.2. The
 27   pre-1.2 interface still exists, but it has been deprecated, and
 28   will be removed in Django 1.4. If you need to maintain an old-style
 29   Django feed, please consult the Django 1.1 documentation. For
 30   details on updating to use the new high-level feed framework, see
 31   the :ref:`Django 1.2 release notes <1.2-updating-feeds>`.
 32
 33Overview
 34--------
 35
 36The high-level feed-generating framework is supplied by the
 37:class:`~django.contrib.syndication.views.Feed` class. To create a
 38feed, write a :class:`~django.contrib.syndication.views.Feed` class
 39and point to an instance of it in your :doc:`URLconf
 40</topics/http/urls>`.
 41
 42Feed classes
 43------------
 44
 45A :class:`~django.contrib.syndication.views.Feed` class is a Python
 46class that represents a syndication feed. A feed can be simple (e.g.,
 47a "site news" feed, or a basic feed displaying the latest entries of a
 48blog) or more complex (e.g., a feed displaying all the blog entries in
 49a particular category, where the category is variable).
 50
 51Feed classes subclass :class:`django.contrib.syndication.views.Feed`.
 52They can live anywhere in your codebase.
 53
 54Instances of :class:`~django.contrib.syndication.views.Feed` classes
 55are views which can be used in your :doc:`URLconf </topics/http/urls>`.
 56
 57A simple example
 58----------------
 59
 60This simple example, taken from `chicagocrime.org`_, describes a feed of the
 61latest five news items::
 62
 63    from django.contrib.syndication.views import Feed
 64    from chicagocrime.models import NewsItem
 65
 66    class LatestEntriesFeed(Feed):
 67        title = "Chicagocrime.org site news"
 68        link = "/sitenews/"
 69        description = "Updates on changes and additions to chicagocrime.org."
 70
 71        def items(self):
 72            return NewsItem.objects.order_by('-pub_date')[:5]
 73
 74        def item_title(self, item):
 75            return item.title
 76
 77        def item_description(self, item):
 78            return item.description
 79
 80To connect a URL to this feed, put an instance of the Feed object in
 81your :doc:`URLconf </topics/http/urls>`. For example::
 82
 83    from django.conf.urls.defaults import *
 84    from myproject.feeds import LatestEntriesFeed
 85
 86    urlpatterns = patterns('',
 87        # ...
 88        (r'^latest/feed/$', LatestEntriesFeed()),
 89        # ...
 90    )
 91
 92Note:
 93
 94* The Feed class subclasses :class:`django.contrib.syndication.views.Feed`.
 95
 96* ``title``, ``link`` and ``description`` correspond to the
 97  standard RSS ``<title>``, ``<link>`` and ``<description>`` elements,
 98  respectively.
 99
100* ``items()`` is, simply, a method that returns a list of objects that
101  should be included in the feed as ``<item>`` elements. Although this
102  example returns ``NewsItem`` objects using Django's
103  :doc:`object-relational mapper </ref/models/querysets>`, ``items()``
104  doesn't have to return model instances. Although you get a few bits of
105  functionality "for free" by using Django models, ``items()`` can
106  return any type of object you want.
107
108* If you're creating an Atom feed, rather than an RSS feed, set the
109  ``subtitle`` attribute instead of the ``description`` attribute.
110  See `Publishing Atom and RSS feeds in tandem`_, later, for an example.
111
112One thing is left to do. In an RSS feed, each ``<item>`` has a ``<title>``,
113``<link>`` and ``<description>``. We need to tell the framework what data to put
114into those elements.
115
116    * For the contents of ``<title>`` and ``<description>``, Django tries
117      calling the methods ``item_title()`` and ``item_description()`` on
118      the :class:`~django.contrib.syndication.views.Feed` class. They are passed
119      a single parameter, ``item``, which is the object itself. These are
120      optional; by default, the unicode representation of the object is used for
121      both.
122
123      If you want to do any special formatting for either the title or
124      description, :doc:`Django templates </topics/templates>` can be used
125      instead. Their paths can be specified with the ``title_template`` and
126      ``description_template`` attributes on the
127      :class:`~django.contrib.syndication.views.Feed` class. The templates are
128      rendered for each item and are passed two template context variables:
129
130         * ``{{ obj }}`` -- The current object (one of whichever objects you
131           returned in ``items()``).
132
133         * ``{{ site }}`` -- A :class:`django.contrib.sites.models.Site` object
134           representing the current site. This is useful for ``{{ site.domain
135           }}`` or ``{{ site.name }}``. If you do *not* have the Django sites
136           framework installed, this will be set to a
137           :class:`django.contrib.sites.models.RequestSite` object. See the
138           :ref:`RequestSite section of the sites framework documentation
139           <requestsite-objects>` for more.
140
141      See `a complex example`_ below that uses a description template.
142
143    * To specify the contents of ``<link>``, you have two options. For each item
144      in ``items()``, Django first tries calling the
145      ``item_link()`` method on the
146      :class:`~django.contrib.syndication.views.Feed` class. In a similar way to
147      the title and description, it is passed it a single parameter,
148      ``item``. If that method doesn't exist, Django tries executing a
149      ``get_absolute_url()`` method on that object. Both
150      ``get_absolute_url()`` and ``item_link()`` should return the
151      item's URL as a normal Python string. As with ``get_absolute_url()``, the
152      result of ``item_link()`` will be included directly in the URL, so you
153      are responsible for doing all necessary URL quoting and conversion to
154      ASCII inside the method itself.
155
156.. _chicagocrime.org: http://www.chicagocrime.org/
157
158A complex example
159-----------------
160
161The framework also supports more complex feeds, via arguments.
162
163For example, `chicagocrime.org`_ offers an RSS feed of recent crimes for every
164police beat in Chicago. It'd be silly to create a separate
165:class:`~django.contrib.syndication.views.Feed` class for each police beat; that
166would violate the :ref:`DRY principle <dry>` and would couple data to
167programming logic. Instead, the syndication framework lets you access the
168arguments passed from your :doc:`URLconf </topics/http/urls>` so feeds can output
169items based on information in the feed's URL.
170
171On chicagocrime.org, the police-beat feeds are accessible via URLs like this:
172
173    * :file:`/beats/613/rss/` -- Returns recent crimes for beat 613.
174    * :file:`/beats/1424/rss/` -- Returns recent crimes for beat 1424.
175
176These can be matched with a :doc:`URLconf </topics/http/urls>` line such as::
177
178    (r'^beats/(?P<beat_id>\d+)/rss/$', BeatFeed()),
179
180Like a view, the arguments in the URL are passed to the ``get_object()``
181method along with the request object.
182
183.. versionchanged:: 1.2
184   Prior to version 1.2, ``get_object()`` only accepted a ``bits`` argument.
185
186Here's the code for these beat-specific feeds::
187
188    from django.contrib.syndication.views import FeedDoesNotExist
189    from django.shortcuts import get_object_or_404
190
191    class BeatFeed(Feed):
192        description_template = 'feeds/beat_description.html'
193
194        def get_object(self, request, beat_id):
195            return get_object_or_404(Beat, pk=beat_id)
196
197        def title(self, obj):
198            return "Chicagocrime.org: Crimes for beat %s" % obj.beat
199
200        def link(self, obj):
201            return obj.get_absolute_url()
202
203        def description(self, obj):
204            return "Crimes recently reported in police beat %s" % obj.beat
205
206        def items(self, obj):
207            return Crime.objects.filter(beat=obj).order_by('-crime_date')[:30]
208
209To generate the feed's ``<title>``, ``<link>`` and ``<description>``, Django
210uses the ``title()``, ``link()`` and ``description()`` methods. In
211the previous example, they were simple string class attributes, but this example
212illustrates that they can be either strings *or* methods. For each of
213``title``, ``link`` and ``description``, Django follows this
214algorithm:
215
216    * First, it tries to call a method, passing the ``obj`` argument, where
217      ``obj`` is the object returned by ``get_object()``.
218
219    * Failing that, it tries to call a method with no arguments.
220
221    * Failing that, it uses the class attribute.
222
223Also note that ``items()`` also follows the same algorithm -- first, it
224tries ``items(obj)``, then ``items()``, then finally an ``items``
225class attribute (which should be a list).
226
227We are using a template for the item descriptions. It can be very simple:
228
229.. code-block:: html+django
230
231    {{ obj.description }}
232
233However, you are free to add formatting as desired.
234
235The ``ExampleFeed`` class below gives full documentation on methods and
236attributes of :class:`~django.contrib.syndication.views.Feed` classes.
237
238Specifying the type of feed
239---------------------------
240
241By default, feeds produced in this framework use RSS 2.0.
242
243To change that, add a ``feed_type`` attribute to your
244:class:`~django.contrib.syndication.views.Feed` class, like so::
245
246    from django.utils.feedgenerator import Atom1Feed
247
248    class MyFeed(Feed):
249        feed_type = Atom1Feed
250
251Note that you set ``feed_type`` to a class object, not an instance.
252
253Currently available feed types are:
254
255    * :class:`django.utils.feedgenerator.Rss201rev2Feed` (RSS 2.01. Default.)
256    * :class:`django.utils.feedgenerator.RssUserland091Feed` (RSS 0.91.)
257    * :class:`django.utils.feedgenerator.Atom1Feed` (Atom 1.0.)
258
259Enclosures
260----------
261
262To specify enclosures, such as those used in creating podcast feeds, use the
263``item_enclosure_url``, ``item_enclosure_length`` and
264``item_enclosure_mime_type`` hooks. See the ``ExampleFeed`` class below for
265usage examples.
266
267Language
268--------
269
270Feeds created by the syndication framework automatically include the
271appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
272comes directly from your :setting:`LANGUAGE_CODE` setting.
273
274URLs
275----
276
277The ``link`` method/attribute can return either an absolute path (e.g.
278:file:`"/blog/"`) or a URL with the fully-qualified domain and protocol (e.g.
279``"http://www.example.com/blog/"``). If ``link`` doesn't return the domain,
280the syndication framework will insert the domain of the current site, according
281to your :setting:`SITE_ID setting <SITE_ID>`.
282
283Atom feeds require a ``<link rel="self">`` that defines the feed's current
284location. The syndication framework populates this automatically, using the
285domain of the current site according to the :setting:`SITE_ID` setting.
286
287Publishing Atom and RSS feeds in tandem
288---------------------------------------
289
290Some developers like to make available both Atom *and* RSS versions of their
291feeds. That's easy to do with Django: Just create a subclass of your
292:class:`~django.contrib.syndication.views.Feed`
293class and set the ``feed_type`` to something different. Then update your
294URLconf to add the extra versions.
295
296Here's a full example::
297
298    from django.contrib.syndication.views import Feed
299    from chicagocrime.models import NewsItem
300    from django.utils.feedgenerator import Atom1Feed
301
302    class RssSiteNewsFeed(Feed):
303        title = "Chicagocrime.org site news"
304        link = "/sitenews/"
305        description = "Updates on changes and additions to chicagocrime.org."
306
307        def items(self):
308            return NewsItem.objects.order_by('-pub_date')[:5]
309
310    class AtomSiteNewsFeed(RssSiteNewsFeed):
311        feed_type = Atom1Feed
312        subtitle = RssSiteNewsFeed.description
313
314.. Note::
315    In this example, the RSS feed uses a ``description`` while the Atom
316    feed uses a ``subtitle``. That's because Atom feeds don't provide for
317    a feed-level "description," but they *do* provide for a "subtitle."
318
319    If you provide a ``description`` in your
320    :class:`~django.contrib.syndication.views.Feed` class, Django will *not*
321    automatically put that into the ``subtitle`` element, because a
322    subtitle and description are not necessarily the same thing. Instead, you
323    should define a ``subtitle`` attribute.
324
325    In the above example, we simply set the Atom feed's ``subtitle`` to the
326    RSS feed's ``description``, because it's quite short already.
327
328And the accompanying URLconf::
329
330    from django.conf.urls.defaults import *
331    from myproject.feeds import RssSiteNewsFeed, AtomSiteNewsFeed
332
333    urlpatterns = patterns('',
334        # ...
335        (r'^sitenews/rss/$', RssSiteNewsFeed()),
336        (r'^sitenews/atom/$', AtomSiteNewsFeed()),
337        # ...
338    )
339
340Feed class reference
341--------------------
342
343.. class:: django.contrib.syndication.views.Feed
344
345This example illustrates all possible attributes and methods for a
346:class:`~django.contrib.syndication.views.Feed` class::
347
348    from django.contrib.syndication.views import Feed
349    from django.utils import feedgenerator
350
351    class ExampleFeed(Feed):
352
353        # FEED TYPE -- Optional. This should be a class that subclasses
354        # django.utils.feedgenerator.SyndicationFeed. This designates
355        # which type of feed this should be: RSS 2.0, Atom 1.0, etc. If
356        # you don't specify feed_type, your feed will be RSS 2.0. This
357        # should be a class, not an instance of the class.
358
359        feed_type = feedgenerator.Rss201rev2Feed
360
361        # TEMPLATE NAMES -- Optional. These should be strings
362        # representing names of Django templates that the system should
363        # use in rendering the title and description of your feed items.
364        # Both are optional. If a template is not specified, the
365        # item_title() or item_description() methods are used instead.
366
367        title_template = None
368        description_template = None
369
370        # TITLE -- One of the following three is required. The framework
371        # looks for them in this order.
372
373        def title(self, obj):
374            """
375            Takes the object returned by get_object() and returns the
376            feed's title as a normal Python string.
377            """
378
379        def title(self):
380            """
381            Returns the feed's title as a normal Python string.
382            """
383
384        title = 'foo' # Hard-coded title.
385
386        # LINK -- One of the following three is required. The framework
387        # looks for them in this order.
388
389        def link(self, obj):
390            """
391            # Takes the object returned by get_object() and returns the feed's
392            # link as a normal Python string.
393            """
394
395        def link(self):
396            """
397            Returns the feed's link as a normal Python string.
398            """
399
400        link = '/foo/bar/' # Hard-coded link.
401
402        # GUID -- One of the following three is optional. The framework looks
403        # for them in this order. This property is only used for Atom feeds
404        # (where it is the feed-level ID element). If not provided, the feed
405        # link is used as the ID.
406
407        def feed_guid(self, obj):
408            """
409            Takes the object returned by get_object() and returns the globally
410            unique ID for the feed as a normal Python string.
411            """
412
413        def feed_guid(self):
414            """
415            Returns the feed's globally unique ID as a normal Python string.
416            """
417
418        feed_guid = '/foo/bar/1234' # Hard-coded guid.
419
420        # DESCRIPTION -- One of the following three is required. The framework
421        # looks for them in this order.
422
423        def description(self, obj):
424            """
425            Takes the object returned by get_object() and returns the feed's
426            description as a normal Python string.
427            """
428
429        def description(self):
430            """
431            Returns the feed's description as a normal Python string.
432            """
433
434        description = 'Foo bar baz.' # Hard-coded description.
435
436        # AUTHOR NAME --One of the following three is optional. The framework
437        # looks for them in this order.
438
439        def author_name(self, obj):
440            """
441            Takes the object returned by get_object() and returns the feed's
442            author's name as a normal Python string.
443            """
444
445        def author_name(self):
446            """
447            Returns the feed's author's name as a normal Python string.
448            """
449
450        author_name = 'Sally Smith' # Hard-coded author name.
451
452        # AUTHOR E-MAIL --One of the following three is optional. The framework
453        # looks for them in this order.
454
455        def author_email(self, obj):
456            """
457            Takes the object returned by get_object() and returns the feed's
458            author's e-mail as a normal Python string.
459            """
460
461        def author_email(self):
462            """
463            Returns the feed's author's e-mail as a normal Python string.
464            """
465
466        author_email = 'test@example.com' # Hard-coded author e-mail.
467
468        # AUTHOR LINK --One of the following three is optional. The framework
469        # looks for them in this order. In each case, the URL should include
470        # the "http://" and domain name.
471
472        def author_link(self, obj):
473            """
474            Takes the object returned by get_object() and returns the feed's
475            author's URL as a normal Python string.
476            """
477
478        def author_link(self):
479            """
480            Returns the feed's author's URL as a normal Python string.
481            """
482
483        author_link = 'http://www.example.com/' # Hard-coded author URL.
484
485        # CATEGORIES -- One of the following three is optional. The framework
486        # looks for them in this order. In each case, the method/attribute
487        # should return an iterable object that returns strings.
488
489        def categories(self, obj):
490            """
491            Takes the object returned by get_object() and returns the feed's
492            categories as iterable over strings.
493            """
494
495        def categories(self):
496            """
497            Returns the feed's categories as iterable over strings.
498            """
499
500        categories = ("python", "django") # Hard-coded list of categories.
501
502        # COPYRIGHT NOTICE -- One of the following three is optional. The
503        # framework looks for them in this order.
504
505        def feed_copyright(self, obj):
506            """
507            Takes the object returned by get_object() and returns the feed's
508            copyright notice as a normal Python string.
509            """
510
511        def feed_copyright(self):
512            """
513            Returns the feed's copyright notice as a normal Python string.
514            """
515
516        feed_copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice.
517
518        # TTL -- One of the following three is optional. The framework looks
519        # for them in this order. Ignored for Atom feeds.
520
521        def ttl(self, obj):
522            """
523            Takes the object returned by get_object() and returns the feed's
524            TTL (Time To Live) as a normal Python string.
525            """
526
527        def ttl(self):
528            """
529            Returns the feed's TTL as a normal Python string.
530            """
531
532        ttl = 600 # Hard-coded Time To Live.
533
534        # ITEMS -- One of the following three is required. The framework looks
535        # for them in this order.
536
537        def items(self, obj):
538            """
539            Takes the object returned by get_object() and returns a list of
540            items to publish in this feed.
541            """
542
543        def items(self):
544            """
545            Returns a list of items to publish in this feed.
546            """
547
548        items = ('Item 1', 'Item 2') # Hard-coded items.
549
550        # GET_OBJECT -- This is required for feeds that publish different data
551        # for different URL parameters. (See "A complex example" above.)
552
553        def get_object(self, request, *args, **kwargs):
554            """
555            Takes the current request and the arguments from the URL, and
556            returns an object represented by this feed. Raises
557            django.core.exceptions.ObjectDoesNotExist on error.
558            """
559
560        # ITEM TITLE AND DESCRIPTION -- If title_template or
561        # description_template are not defined, these are used instead. Both are
562        # optional, by default they will use the unicode representation of the
563        # item.
564
565        def item_title(self, item):
566            """
567            Takes an item, as returned by items(), and returns the item's
568            title as a normal Python string.
569            """
570
571        def item_title(self):
572            """
573            Returns the title for every item in the feed.
574            """
575
576        item_title = 'Breaking News: Nothing Happening' # Hard-coded title.
577
578        def item_description(self, item):
579            """
580            Takes an item, as returned by items(), and returns the item's
581            description as a normal Python string.
582            """
583
584        def item_description(self):
585            """
586            Returns the description for every item in the feed.
587            """
588
589        item_description = 'A description of the item.' # Hard-coded description.
590
591        # ITEM LINK -- One of these three is required. The framework looks for
592        # them in this order.
593
594        # First, the framework tries the two methods below, in
595        # order. Failing that, it falls back to the get_absolute_url()
596        # method on each item returned by items().
597
598        def item_link(self, item):
599            """
600            Takes an item, as returned by items(), and returns the item's URL.
601            """
602
603        def item_link(self):
604            """
605            Returns the URL for every item in the feed.
606            """
607
608        # ITEM_GUID -- The following method is optional. If not provided, the
609        # item's link is used by default.
610
611        def item_guid(self, obj):
612            """
613            Takes an item, as return by items(), and returns the item's ID.
614            """
615
616        # ITEM AUTHOR NAME -- One of the following three is optional. The
617        # framework looks for them in this order.
618
619        def item_author_name(self, item):
620            """
621            Takes an item, as returned by items(), and returns the item's
622            author's name as a normal Python string.
623            """
624
625        def item_author_name(self):
626            """
627            Returns the author name for every item in the feed.
628            """
629
630        item_author_name = 'Sally Smith' # Hard-coded author name.
631
632        # ITEM AUTHOR E-MAIL --One of the following three is optional. The
633        # framework looks for them in this order.
634        #
635        # If you specify this, you must specify item_author_name.
636
637        def item_author_email(self, obj):
638            """
639            Takes an item, as returned by items(), and returns the item's
640            author's e-mail as a normal Python string.
641            """
642
643        def item_author_email(self):
644            """
645            Returns the author e-mail for every item in the feed.
646            """
647
648        item_author_email = 'test@example.com' # Hard-coded author e-mail.
649
650        # ITEM AUTHOR LINK -- One of the following three is optional. The
651        # framework looks for them in this order. In each case, the URL should
652        # include the "http://" and domain name.
653        #
654        # If you specify this, you must specify item_author_name.
655
656        def item_author_link(self, obj):
657            """
658            Takes an item, as returned by items(), and returns the item's
659            author's URL as a normal Python string.
660            """
661
662        def item_author_link(self):
663            """
664            Returns the author URL for every item in the feed.
665            """
666
667        item_author_link = 'http://www.example.com/' # Hard-coded author URL.
668
669        # ITEM ENCLOSURE URL -- One of these three is required if you're
670        # publishing enclosures. The framework looks for them in this order.
671
672        def item_enclosure_url(self, item):
673            """
674            Takes an item, as returned by items(), and returns the item's
675            enclosure URL.
676            """
677
678        def item_enclosure_url(self):
679            """
680            Returns the enclosure URL for every item in the feed.
681            """
682
683        item_enclosure_url = "/foo/bar.mp3" # Hard-coded enclosure link.
684
685        # ITEM ENCLOSURE LENGTH -- One of these three is required if you're
686        # publishing enclosures. The framework looks for them in this order.
687        # In each case, the returned value should be either an integer, or a
688        # string representation of the integer, in bytes.
689
690        def item_enclosure_length(self, item):
691            """
692            Takes an item, as returned by items(), and returns the item's
693            enclosure length.
694            """
695
696        def item_enclosure_length(self):
697            """
698            Returns the enclosure length for every item in the feed.
699            """
700
701        item_enclosure_length = 32000 # Hard-coded enclosure length.
702
703        # ITEM ENCLOSURE MIME TYPE -- One of these three is required if you're
704        # publishing enclosures. The framework looks for them in this order.
705
706        def item_enclosure_mime_type(self, item):
707            """
708            Takes an item, as returned by items(), and returns the item's
709            enclosure MIME type.
710            """
711
712        def item_enclosure_mime_type(self):
713            """
714            Returns the enclosure MIME type for every item in the feed.
715            """
716
717        item_enclosure_mime_type = "audio/mpeg" # Hard-coded enclosure MIME type.
718
719        # ITEM PUBDATE -- It's optional to use one of these three. This is a
720        # hook that specifies how to get the pubdate for a given item.
721        # In each case, the method/attribute should return a Python
722        # datetime.datetime object.
723
724        def item_pubdate(self, item):
725            """
726            Takes an item, as returned by items(), and returns the item's
727            pubdate.
728            """
729
730        def item_pubdate(self):
731            """
732            Returns the pubdate for every item in the feed.
733            """
734
735        item_pubdate = datetime.datetime(2005, 5, 3) # Hard-coded pubdate.
736
737        # ITEM CATEGORIES -- It's optional to use one of these three. This is
738        # a hook that specifies how to get the list of categories for a given
739        # item. In each case, the method/attribute should return an iterable
740        # object that returns strings.
741
742        def item_categories(self, item):
743            """
744            Takes an item, as returned by items(), and returns the item's
745            categories.
746            """
747
748        def item_categories(self):
749            """
750            Returns the categories for every item in the feed.
751            """
752
753        item_categories = ("python", "django") # Hard-coded categories.
754
755        # ITEM COPYRIGHT NOTICE (only applicable to Atom feeds) -- One of the
756        # following three is optional. The framework looks for them in this
757        # order.
758
759        def item_copyright(self, obj):
760            """
761            Takes an item, as returned by items(), and returns the item's
762            copyright notice as a normal Python string.
763            """
764
765        def item_copyright(self):
766            """
767            Returns the copyright notice for every item in the feed.
768            """
769
770        item_copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice.
771
772
773The low-level framework
774=======================
775
776Behind the scenes, the high-level RSS framework uses a lower-level framework
777for generating feeds' XML. This framework lives in a single module:
778`django/utils/feedgenerator.py`_.
779
780You use this framework on your own, for lower-level feed generation. You can
781also create custom feed generator subclasses for use with the ``feed_type``
782``Feed`` option.
783
784.. currentmodule:: django.utils.feedgenerator
785
786``SyndicationFeed`` classes
787---------------------------
788
789The :mod:`~django.utils.feedgenerator` module contains a base class:
790
791  * :class:`django.utils.feedgenerator.SyndicationFeed`
792
793and several subclasses:
794
795  * :class:`django.utils.feedgenerator.RssUserland091Feed`
796  * :class:`django.utils.feedgenerator.Rss201rev2Feed`
797  * :class:`django.utils.feedgenerator.Atom1Feed`
798
799Each of these three classes knows how to render a certain type of feed as XML.
800They share this interface:
801
802:meth:`.SyndicationFeed.__init__`
803    Initialize the feed with the given dictionary of metadata, which applies to
804    the entire feed. Required keyword arguments are:
805
806        * ``title``
807        * ``link``
808        * ``description``
809
810    There's also a bunch of other optional keywords:
811
812        * ``language``
813        * ``author_email``
814        * ``author_name``
815        * ``author_link``
816        * ``subtitle``
817        * ``categories``
818        * ``feed_url``
819        * ``feed_copyright``
820        * ``feed_guid``
821        * ``ttl``
822
823    Any extra keyword arguments you pass to ``__init__`` will be stored in
824    ``self.feed`` for use with `custom feed generators`_.
825
826    All parameters should be Unicode objects, except ``categories``, which
827    should be a sequence of Unicode objects.
828
829:meth:`.SyndicationFeed.add_item`
830    Add an item to the feed with the given parameters.
831
832    Required keyword arguments are:
833
834        * ``title``
835        * ``link``
836        * ``description``
837
838    Optional keyword arguments are:
839
840        * ``author_email``
841        * ``author_name``
842        * ``author_link``
843        * ``pubdate``
844        * ``comments``
845        * ``unique_id``
846        * ``enclosure``
847        * ``categories``
848        * ``item_copyright``
849        * ``ttl``
850
851    Extra keyword arguments will be stored for `custom feed generators`_.
852
853    All parameters, if given, should be Unicode objects, except:
854
855        * ``pubdate`` should be a `Python datetime object`_.
856        * ``enclosure`` should be an instance of ``feedgenerator.Enclosure``.
857        * ``categories`` should be a sequence of Unicode objects.
858
859:meth:`.SyndicationFeed.write`
860    Outputs the feed in the given encoding to outfile, which is a file-like object.
861
862:meth:`.SyndicationFeed.writeString`
863    Returns the feed as a string in the given encoding.
864
865For example, to create an Atom 1.0 feed and print it to standard output::
866
867    >>> from django.utils import feedgenerator
868    >>> from datetime import datetime
869    >>> f = feedgenerator.Atom1Feed(
870    ...     title=u"My Weblog",
871    ...     link=u"http://www.example.com/",
872    ...     description=u"In which I write about what I ate today.",
873    ...     language=u"en",
874    ...     author_name=u"Myself",
875    ...     feed_url=u"http://example.com/atom.xml")
876    >>> f.add_item(title=u"Hot dog today",
877    ...     link=u"http://www.example.com/entries/1/",
878    ...     pubdate=datetime.now(),
879    ...     description=u"<p>Today I had a Vienna Beef hot dog. It was pink, plump and perfect.</p>")
880    >>> print f.writeString('UTF-8')
881    <?xml version="1.0" encoding="UTF-8"?>
882    <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
883    ...
884    </feed>
885
886.. _django/utils/feedgenerator.py: http://code.djangoproject.com/browser/django/trunk/django/utils/feedgenerator.py
887.. _Python datetime object: http://docs.python.org/library/datetime.html#datetime-objects
888
889.. currentmodule:: django.contrib.syndication
890
891Custom feed generators
892----------------------
893
894If you need to produce a custom feed format, you've got a couple of options.
895
896If the feed format is totally custom, you'll want to subclass
897``SyndicationFeed`` and completely replace the ``write()`` and
898``writeString()`` methods.
899
900However, if the feed format is a spin-off of RSS or Atom (i.e. GeoRSS_, Apple's
901`iTunes podcast format`_, etc.), you've got a better choice. These types of
902feeds typically add extra elements and/or attributes to the underlying format,
903and there are a set of methods that ``SyndicationFeed`` calls to get these extra
904attributes. Thus, you can subclass the appropriate feed generator class
905(``Atom1Feed`` or ``Rss201rev2Feed``) and extend these callbacks. They are:
906
907.. _georss: http://georss.org/
908.. _itunes podcast format: http://www.apple.com/itunes/podcasts/specs.html
909
910``SyndicationFeed.root_attributes(self, )``
911    Return a ``dict`` of attributes to add to the root feed element
912    (``feed``/``channel``).
913
914``SyndicationFeed.add_root_elements(self, handler)``
915    Callback to add elements inside the root feed element
916    (``feed``/``channel``). ``handler`` is an `XMLGenerator`_ from Python's
917    built-in SAX library; you'll call methods on it to add to the XML
918    document in process.
919
920``SyndicationFeed.item_attributes(self, item)``
921    Return a ``dict`` of attributes to add to each item (``item``/``entry``)
922    element. The argument, ``item``, is a dictionary of all the data passed to
923    ``SyndicationFeed.add_item()``.
924
925``SyndicationFeed.add_item_elements(self, handler, item)``
926    Callback to add elements to each item (``item``/``entry``) element.
927    ``handler`` and ``item`` are as above.
928
929.. warning::
930
931    If you override any of these methods, be sure to call the superclass methods
932    since they add the required elements for each feed format.
933
934For example, you might start implementing an iTunes RSS feed generator like so::
935
936    class iTunesFeed(Rss201rev2Feed):
937        def root_attributes(self):
938            attrs = super(iTunesFeed, self).root_attributes()
939            attrs['xmlns:itunes'] = 'http://www.itunes.com/dtds/podcast-1.0.dtd'
940            return attrs
941
942        def add_root_elements(self, handler):
943            super(iTunesFeed, self).add_root_elements(handler)
944            handler.addQuickElement('itunes:explicit', 'clean')
945
946Obviously there's a lot more work to be done for a complete custom feed class,
947but the above example should demonstrate the basic idea.
948
949.. _XMLGenerator: http://docs.python.org/dev/library/xml.sax.utils.html#xml.sax.saxutils.XMLGenerator