Ticket #6039: 6039.patch

File 6039.patch, 67.2 KB (added by John DeRosa, 16 years ago)

Patch for this ticket.

  • docs/syndication_feeds.txt

     
    1 ==============================
    2 The syndication feed framework
    3 ==============================
    4 
    5 Django comes with a high-level syndication-feed-generating framework that makes
    6 creating RSS_ and Atom_ feeds easy.
    7 
    8 To create any syndication feed, all you have to do is write a short Python
    9 class. You can create as many feeds as you want.
    10 
    11 Django also comes with a lower-level feed-generating API. Use this if you want
    12 to generate feeds outside of a Web context, or in some other lower-level way.
    13 
    14 .. _RSS: http://www.whatisrss.com/
    15 .. _Atom: http://www.atomenabled.org/
    16 
    17 The high-level framework
    18 ========================
    19 
    20 Overview
    21 --------
    22 
    23 The high-level feed-generating framework is a view that's hooked to ``/feeds/``
    24 by default. Django uses the remainder of the URL (everything after ``/feeds/``)
    25 to determine which feed to output.
    26 
    27 To create a feed, just write a ``Feed`` class and point to it in your URLconf_.
    28 
    29 .. _URLconf: ../url_dispatch/
    30 
    31 Initialization
    32 --------------
    33 
    34 If you're not using the latest Django development version, you'll need to make
    35 sure Django's sites framework is installed -- including its database table.
    36 (See the `sites framework documentation`_ for more information.) This has
    37 changed in the Django development version; the syndication feed framework no
    38 longer requires the sites framework.
    39 
    40 To activate syndication feeds on your Django site, add this line to your
    41 URLconf_::
    42 
    43     (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}),
    44 
    45 This tells Django to use the RSS framework to handle all URLs starting with
    46 ``"feeds/"``. (You can change that ``"feeds/"`` prefix to fit your own needs.)
    47 
    48 This URLconf line has an extra argument: ``{'feed_dict': feeds}``. Use this
    49 extra argument to pass the syndication framework the feeds that should be
    50 published under that URL.
    51 
    52 Specifically, ``feed_dict`` should be a dictionary that maps a feed's slug
    53 (short URL label) to its ``Feed`` class.
    54 
    55 You can define the ``feed_dict`` in the URLconf itself. Here's a full example
    56 URLconf::
    57 
    58     from django.conf.urls.defaults import *
    59     from myproject.feeds import LatestEntries, LatestEntriesByCategory
    60 
    61     feeds = {
    62         'latest': LatestEntries,
    63         'categories': LatestEntriesByCategory,
    64     }
    65 
    66     urlpatterns = patterns('',
    67         # ...
    68         (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
    69             {'feed_dict': feeds}),
    70         # ...
    71     )
    72 
    73 The above example registers two feeds:
    74 
    75     * The feed represented by ``LatestEntries`` will live at ``feeds/latest/``.
    76     * The feed represented by ``LatestEntriesByCategory`` will live at
    77       ``feeds/categories/``.
    78 
    79 Once that's set up, you just need to define the ``Feed`` classes themselves.
    80 
    81 .. _sites framework documentation: ../sites/
    82 .. _URLconf: ../url_dispatch/
    83 .. _settings file: ../settings/
    84 
    85 Feed classes
    86 ------------
    87 
    88 A ``Feed`` class is a simple Python class that represents a syndication feed.
    89 A feed can be simple (e.g., a "site news" feed, or a basic feed displaying
    90 the latest entries of a blog) or more complex (e.g., a feed displaying all the
    91 blog entries in a particular category, where the category is variable).
    92 
    93 ``Feed`` classes must subclass ``django.contrib.syndication.feeds.Feed``. They
    94 can live anywhere in your codebase.
    95 
    96 A simple example
    97 ----------------
    98 
    99 This simple example, taken from `chicagocrime.org`_, describes a feed of the
    100 latest five news items::
    101 
    102     from django.contrib.syndication.feeds import Feed
    103     from chicagocrime.models import NewsItem
    104 
    105     class LatestEntries(Feed):
    106         title = "Chicagocrime.org site news"
    107         link = "/sitenews/"
    108         description = "Updates on changes and additions to chicagocrime.org."
    109 
    110         def items(self):
    111             return NewsItem.objects.order_by('-pub_date')[:5]
    112 
    113 Note:
    114 
    115     * The class subclasses ``django.contrib.syndication.feeds.Feed``.
    116     * ``title``, ``link`` and ``description`` correspond to the standard
    117       RSS ``<title>``, ``<link>`` and ``<description>`` elements, respectively.
    118     * ``items()`` is, simply, a method that returns a list of objects that
    119       should be included in the feed as ``<item>`` elements. Although this
    120       example returns ``NewsItem`` objects using Django's
    121       `object-relational mapper`_, ``items()`` doesn't have to return model
    122       instances. Although you get a few bits of functionality "for free" by
    123       using Django models, ``items()`` can return any type of object you want.
    124     * If you're creating an Atom feed, rather than an RSS feed, set the
    125       ``subtitle`` attribute instead of the ``description`` attribute. See
    126       `Publishing Atom and RSS feeds in tandem`_, later, for an example.
    127 
    128 One thing's left to do. In an RSS feed, each ``<item>`` has a ``<title>``,
    129 ``<link>`` and ``<description>``. We need to tell the framework what data to
    130 put into those elements.
    131 
    132     * To specify the contents of ``<title>`` and ``<description>``, create
    133       `Django templates`_ called ``feeds/latest_title.html`` and
    134       ``feeds/latest_description.html``, where ``latest`` is the ``slug``
    135       specified in the URLconf for the given feed. Note the ``.html`` extension
    136       is required. The RSS system renders that template for each item, passing
    137       it two template context variables:
    138 
    139           * ``{{ obj }}`` -- The current object (one of whichever objects you
    140             returned in ``items()``).
    141           * ``{{ site }}`` -- A ``django.contrib.sites.models.Site`` object
    142             representing the current site. This is useful for
    143             ``{{ site.domain }}`` or ``{{ site.name }}``. Note that if you're
    144             using the latest Django development version and do *not* have the
    145             Django sites framework installed, this will be set to a
    146             ``django.contrib.sites.models.RequestSite`` object. See the
    147             `RequestSite section of the sites framework documentation`_ for
    148             more.
    149 
    150       If you don't create a template for either the title or description, the
    151       framework will use the template ``"{{ obj }}"`` by default -- that is,
    152       the normal string representation of the object. You can also change the
    153       names of these two templates by specifying ``title_template`` and
    154       ``description_template`` as attributes of your ``Feed`` class.
    155     * To specify the contents of ``<link>``, you have two options. For each
    156       item in ``items()``, Django first tries executing a
    157       ``get_absolute_url()`` method on that object. If that method doesn't
    158       exist, it tries calling a method ``item_link()`` in the ``Feed`` class,
    159       passing it a single parameter, ``item``, which is the object itself.
    160       Both ``get_absolute_url()`` and ``item_link()`` should return the item's
    161       URL as a normal Python string. As with ``get_absolute_url()``, the
    162       result of ``item_link()`` will be included directly in the URL, so you
    163       are responsible for doing all necessary URL quoting and conversion to
    164       ASCII inside the method itself.
    165 
    166     * For the LatestEntries example above, we could have very simple feed templates:
    167 
    168           * latest_title.html::
    169 
    170              {{ obj.title }}
    171 
    172           * latest_description.html::
    173 
    174              {{ obj.description }}
    175 
    176 .. _chicagocrime.org: http://www.chicagocrime.org/
    177 .. _object-relational mapper: ../db-api/
    178 .. _Django templates: ../templates/
    179 .. _RequestSite section of the sites framework documentation: ../sites/#requestsite-objects
    180 
    181 A complex example
    182 -----------------
    183 
    184 The framework also supports more complex feeds, via parameters.
    185 
    186 For example, `chicagocrime.org`_ offers an RSS feed of recent crimes for every
    187 police beat in Chicago. It'd be silly to create a separate ``Feed`` class for
    188 each police beat; that would violate the `DRY principle`_ and would couple data
    189 to programming logic. Instead, the syndication framework lets you make generic
    190 feeds that output items based on information in the feed's URL.
    191 
    192 On chicagocrime.org, the police-beat feeds are accessible via URLs like this:
    193 
    194     * ``/rss/beats/0613/`` -- Returns recent crimes for beat 0613.
    195     * ``/rss/beats/1424/`` -- Returns recent crimes for beat 1424.
    196 
    197 The slug here is ``"beats"``. The syndication framework sees the extra URL bits
    198 after the slug -- ``0613`` and ``1424`` -- and gives you a hook to tell it what
    199 those URL bits mean, and how they should influence which items get published in
    200 the feed.
    201 
    202 An example makes this clear. Here's the code for these beat-specific feeds::
    203 
    204     from django.contrib.syndication.feeds import FeedDoesNotExist
    205 
    206     class BeatFeed(Feed):
    207         def get_object(self, bits):
    208             # In case of "/rss/beats/0613/foo/bar/baz/", or other such clutter,
    209             # check that bits has only one member.
    210             if len(bits) != 1:
    211                 raise ObjectDoesNotExist
    212             return Beat.objects.get(beat__exact=bits[0])
    213 
    214         def title(self, obj):
    215             return "Chicagocrime.org: Crimes for beat %s" % obj.beat
    216 
    217         def link(self, obj):
    218             if not obj:
    219                 raise FeedDoesNotExist
    220             return obj.get_absolute_url()
    221 
    222         def description(self, obj):
    223             return "Crimes recently reported in police beat %s" % obj.beat
    224 
    225         def items(self, obj):
    226             return Crime.objects.filter(beat__id__exact=obj.id).order_by('-crime_date')[:30]
    227 
    228 Here's the basic algorithm the RSS framework follows, given this class and a
    229 request to the URL ``/rss/beats/0613/``:
    230 
    231     * The framework gets the URL ``/rss/beats/0613/`` and notices there's
    232       an extra bit of URL after the slug. It splits that remaining string by
    233       the slash character (``"/"``) and calls the ``Feed`` class'
    234       ``get_object()`` method, passing it the bits. In this case, bits is
    235       ``['0613']``. For a request to ``/rss/beats/0613/foo/bar/``, bits would
    236       be ``['0613', 'foo', 'bar']``.
    237 
    238     * ``get_object()`` is responsible for retrieving the given beat, from the
    239       given ``bits``. In this case, it uses the Django database API to retrieve
    240       the beat. Note that ``get_object()`` should raise
    241       ``django.core.exceptions.ObjectDoesNotExist`` if given invalid
    242       parameters. There's no ``try``/``except`` around the
    243       ``Beat.objects.get()`` call, because it's not necessary; that function
    244       raises ``Beat.DoesNotExist`` on failure, and ``Beat.DoesNotExist`` is a
    245       subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in
    246       ``get_object()`` tells Django to produce a 404 error for that request.
    247 
    248     * To generate the feed's ``<title>``, ``<link>`` and ``<description>``,
    249       Django uses the ``title()``, ``link()`` and ``description()`` methods. In
    250       the previous example, they were simple string class attributes, but this
    251       example illustrates that they can be either strings *or* methods. For
    252       each of ``title``, ``link`` and ``description``, Django follows this
    253       algorithm:
    254 
    255           * First, it tries to call a method, passing the ``obj`` argument,
    256             where ``obj`` is the object returned by ``get_object()``.
    257           * Failing that, it tries to call a method with no arguments.
    258           * Failing that, it uses the class attribute.
    259 
    260       Inside the ``link()`` method, we handle the possibility that ``obj``
    261       might be ``None``, which can occur when the URL isn't fully specified. In
    262       some cases, you might want to do something else in this case, which would
    263       mean you'd need to check for ``obj`` existing in other methods as well.
    264       (The ``link()`` method is called very early in the feed generation
    265       process, so it's a good place to bail out early.)
    266 
    267     * Finally, note that ``items()`` in this example also takes the ``obj``
    268       argument. The algorithm for ``items`` is the same as described in the
    269       previous step -- first, it tries ``items(obj)``, then ``items()``, then
    270       finally an ``items`` class attribute (which should be a list).
    271 
    272 The ``ExampleFeed`` class below gives full documentation on methods and
    273 attributes of ``Feed`` classes.
    274 
    275 .. _DRY principle: http://c2.com/cgi/wiki?DontRepeatYourself
    276 
    277 Specifying the type of feed
    278 ---------------------------
    279 
    280 By default, feeds produced in this framework use RSS 2.0.
    281 
    282 To change that, add a ``feed_type`` attribute to your ``Feed`` class, like so::
    283 
    284     from django.utils.feedgenerator import Atom1Feed
    285 
    286     class MyFeed(Feed):
    287         feed_type = Atom1Feed
    288 
    289 Note that you set ``feed_type`` to a class object, not an instance.
    290 
    291 Currently available feed types are:
    292 
    293     * ``django.utils.feedgenerator.Rss201rev2Feed`` (RSS 2.01. Default.)
    294     * ``django.utils.feedgenerator.RssUserland091Feed`` (RSS 0.91.)
    295     * ``django.utils.feedgenerator.Atom1Feed`` (Atom 1.0.)
    296 
    297 Enclosures
    298 ----------
    299 
    300 To specify enclosures, such as those used in creating podcast feeds, use the
    301 ``item_enclosure_url``, ``item_enclosure_length`` and
    302 ``item_enclosure_mime_type`` hooks. See the ``ExampleFeed`` class below for
    303 usage examples.
    304 
    305 Language
    306 --------
    307 
    308 Feeds created by the syndication framework automatically include the
    309 appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
    310 comes directly from your `LANGUAGE_CODE setting`_.
    311 
    312 .. _LANGUAGE_CODE setting: ../settings/#language-code
    313 
    314 URLs
    315 ----
    316 
    317 The ``link`` method/attribute can return either an absolute URL (e.g.
    318 ``"/blog/"``) or a URL with the fully-qualified domain and protocol (e.g.
    319 ``"http://www.example.com/blog/"``). If ``link`` doesn't return the domain,
    320 the syndication framework will insert the domain of the current site, according
    321 to your `SITE_ID setting`_.
    322 
    323 Atom feeds require a ``<link rel="self">`` that defines the feed's current
    324 location. The syndication framework populates this automatically, using the
    325 domain of the current site according to the SITE_ID setting.
    326 
    327 .. _SITE_ID setting: ../settings/#site-id
    328 
    329 Publishing Atom and RSS feeds in tandem
    330 ---------------------------------------
    331 
    332 Some developers like to make available both Atom *and* RSS versions of their
    333 feeds. That's easy to do with Django: Just create a subclass of your ``Feed``
    334 class and set the ``feed_type`` to something different. Then update your
    335 URLconf to add the extra versions.
    336 
    337 Here's a full example::
    338 
    339     from django.contrib.syndication.feeds import Feed
    340     from chicagocrime.models import NewsItem
    341     from django.utils.feedgenerator import Atom1Feed
    342 
    343     class RssSiteNewsFeed(Feed):
    344         title = "Chicagocrime.org site news"
    345         link = "/sitenews/"
    346         description = "Updates on changes and additions to chicagocrime.org."
    347 
    348         def items(self):
    349             return NewsItem.objects.order_by('-pub_date')[:5]
    350 
    351     class AtomSiteNewsFeed(RssSiteNewsFeed):
    352         feed_type = Atom1Feed
    353         subtitle = RssSiteNewsFeed.description
    354 
    355 .. Note::
    356     In this example, the RSS feed uses a ``description`` while the Atom feed
    357     uses a ``subtitle``. That's because Atom feeds don't provide for a
    358     feed-level "description," but they *do* provide for a "subtitle."
    359 
    360     If you provide a ``description`` in your ``Feed`` class, Django will *not*
    361     automatically put that into the ``subtitle`` element, because a subtitle
    362     and description are not necessarily the same thing. Instead, you should
    363     define a ``subtitle`` attribute.
    364 
    365     In the above example, we simply set the Atom feed's ``subtitle`` to the
    366     RSS feed's ``description``, because it's quite short already.
    367 
    368 And the accompanying URLconf::
    369 
    370     from django.conf.urls.defaults import *
    371     from myproject.feeds import RssSiteNewsFeed, AtomSiteNewsFeed
    372 
    373     feeds = {
    374         'rss': RssSiteNewsFeed,
    375         'atom': AtomSiteNewsFeed,
    376     }
    377 
    378     urlpatterns = patterns('',
    379         # ...
    380         (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
    381             {'feed_dict': feeds}),
    382         # ...
    383     )
    384 
    385 Feed class reference
    386 --------------------
    387 
    388 This example illustrates all possible attributes and methods for a ``Feed`` class::
    389 
    390     from django.contrib.syndication.feeds import Feed
    391     from django.utils import feedgenerator
    392 
    393     class ExampleFeed(Feed):
    394 
    395         # FEED TYPE -- Optional. This should be a class that subclasses
    396         # django.utils.feedgenerator.SyndicationFeed. This designates which
    397         # type of feed this should be: RSS 2.0, Atom 1.0, etc.
    398         # If you don't specify feed_type, your feed will be RSS 2.0.
    399         # This should be a class, not an instance of the class.
    400 
    401         feed_type = feedgenerator.Rss201rev2Feed
    402 
    403         # TEMPLATE NAMES -- Optional. These should be strings representing
    404         # names of Django templates that the system should use in rendering the
    405         # title and description of your feed items. Both are optional.
    406         # If you don't specify one, or either, Django will use the template
    407         # 'feeds/SLUG_title.html' and 'feeds/SLUG_description.html', where SLUG
    408         # is the slug you specify in the URL.
    409 
    410         title_template = None
    411         description_template = None
    412 
    413         # TITLE -- One of the following three is required. The framework looks
    414         # for them in this order.
    415 
    416         def title(self, obj):
    417             """
    418             Takes the object returned by get_object() and returns the feed's
    419             title as a normal Python string.
    420             """
    421 
    422         def title(self):
    423             """
    424             Returns the feed's title as a normal Python string.
    425             """
    426 
    427         title = 'foo' # Hard-coded title.
    428 
    429         # LINK -- One of the following three is required. The framework looks
    430         # for them in this order.
    431 
    432         def link(self, obj):
    433             """
    434             Takes the object returned by get_object() and returns the feed's
    435             link as a normal Python string.
    436             """
    437 
    438         def link(self):
    439             """
    440             Returns the feed's link as a normal Python string.
    441             """
    442 
    443         link = '/foo/bar/' # Hard-coded link.
    444 
    445         # GUID -- One of the following three is optional. The framework looks
    446         # for them in this order. This property is only used for Atom feeds
    447         # (where it is the feed-level ID element). If not provided, the feed
    448         # link is used as the ID.
    449         #
    450         # (New in Django development version)
    451 
    452         def feed_guid(self, obj):
    453             """
    454             Takes the object returned by get_object() and returns the globally
    455             unique ID for the feed as a normal Python string.
    456             """
    457 
    458         def feed_guid(self):
    459             """
    460             Returns the feed's globally unique ID as a normal Python string.
    461             """
    462 
    463         feed_guid = '/foo/bar/1234' # Hard-coded guid.
    464 
    465         # DESCRIPTION -- One of the following three is required. The framework
    466         # looks for them in this order.
    467 
    468         def description(self, obj):
    469             """
    470             Takes the object returned by get_object() and returns the feed's
    471             description as a normal Python string.
    472             """
    473 
    474         def description(self):
    475             """
    476             Returns the feed's description as a normal Python string.
    477             """
    478 
    479         description = 'Foo bar baz.' # Hard-coded description.
    480 
    481         # AUTHOR NAME --One of the following three is optional. The framework
    482         # looks for them in this order.
    483 
    484         def author_name(self, obj):
    485             """
    486             Takes the object returned by get_object() and returns the feed's
    487             author's name as a normal Python string.
    488             """
    489 
    490         def author_name(self):
    491             """
    492             Returns the feed's author's name as a normal Python string.
    493             """
    494 
    495         author_name = 'Sally Smith' # Hard-coded author name.
    496 
    497         # AUTHOR E-MAIL --One of the following three is optional. The framework
    498         # looks for them in this order.
    499 
    500         def author_email(self, obj):
    501             """
    502             Takes the object returned by get_object() and returns the feed's
    503             author's e-mail as a normal Python string.
    504             """
    505 
    506         def author_email(self):
    507             """
    508             Returns the feed's author's e-mail as a normal Python string.
    509             """
    510 
    511         author_email = 'test@example.com' # Hard-coded author e-mail.
    512 
    513         # AUTHOR LINK --One of the following three is optional. The framework
    514         # looks for them in this order. In each case, the URL should include
    515         # the "http://" and domain name.
    516 
    517         def author_link(self, obj):
    518             """
    519             Takes the object returned by get_object() and returns the feed's
    520             author's URL as a normal Python string.
    521             """
    522 
    523         def author_link(self):
    524             """
    525             Returns the feed's author's URL as a normal Python string.
    526             """
    527 
    528         author_link = 'http://www.example.com/' # Hard-coded author URL.
    529 
    530         # CATEGORIES -- One of the following three is optional. The framework
    531         # looks for them in this order. In each case, the method/attribute
    532         # should return an iterable object that returns strings.
    533 
    534         def categories(self, obj):
    535             """
    536             Takes the object returned by get_object() and returns the feed's
    537             categories as iterable over strings.
    538             """
    539 
    540         def categories(self):
    541             """
    542             Returns the feed's categories as iterable over strings.
    543             """
    544 
    545         categories = ("python", "django") # Hard-coded list of categories.
    546 
    547         # COPYRIGHT NOTICE -- One of the following three is optional. The
    548         # framework looks for them in this order.
    549 
    550         def copyright(self, obj):
    551             """
    552             Takes the object returned by get_object() and returns the feed's
    553             copyright notice as a normal Python string.
    554             """
    555 
    556         def copyright(self):
    557             """
    558             Returns the feed's copyright notice as a normal Python string.
    559             """
    560 
    561         copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice.
    562 
    563         # TTL -- One of the following three is optional. The framework looks
    564         # for them in this order. Ignored for Atom feeds.
    565 
    566         def ttl(self, obj):
    567             """
    568             Takes the object returned by get_object() and returns the feed's
    569             TTL (Time To Live) as a normal Python string.
    570             """
    571 
    572         def ttl(self):
    573             """
    574             Returns the feed's TTL as a normal Python string.
    575             """
    576 
    577         ttl = 600 # Hard-coded Time To Live.
    578 
    579         # ITEMS -- One of the following three is required. The framework looks
    580         # for them in this order.
    581 
    582         def items(self, obj):
    583             """
    584             Takes the object returned by get_object() and returns a list of
    585             items to publish in this feed.
    586             """
    587 
    588         def items(self):
    589             """
    590             Returns a list of items to publish in this feed.
    591             """
    592 
    593         items = ('Item 1', 'Item 2') # Hard-coded items.
    594 
    595         # GET_OBJECT -- This is required for feeds that publish different data
    596         # for different URL parameters. (See "A complex example" above.)
    597 
    598         def get_object(self, bits):
    599             """
    600             Takes a list of strings gleaned from the URL and returns an object
    601             represented by this feed. Raises
    602             django.core.exceptions.ObjectDoesNotExist on error.
    603             """
    604 
    605         # ITEM LINK -- One of these three is required. The framework looks for
    606         # them in this order.
    607 
    608         # First, the framework tries the two methods below, in
    609         # order. Failing that, it falls back to the get_absolute_url()
    610         # method on each item returned by items().
    611 
    612         def item_link(self, item):
    613             """
    614             Takes an item, as returned by items(), and returns the item's URL.
    615             """
    616 
    617         def item_link(self):
    618             """
    619             Returns the URL for every item in the feed.
    620             """
    621 
    622         # ITEM_GUID -- The following method is optional. This property is
    623         # only used for Atom feeds (it is the ID element for an item in an
    624         # Atom feed). If not provided, the item's link is used by default.
    625         #
    626         # (New in Django development version)
    627 
    628         def item_guid(self, obj):
    629             """
    630             Takes an item, as return by items(), and returns the item's ID.
    631             """
    632 
    633         # ITEM AUTHOR NAME -- One of the following three is optional. The
    634         # framework looks for them in this order.
    635 
    636         def item_author_name(self, item):
    637             """
    638             Takes an item, as returned by items(), and returns the item's
    639             author's name as a normal Python string.
    640             """
    641 
    642         def item_author_name(self):
    643             """
    644             Returns the author name for every item in the feed.
    645             """
    646 
    647         item_author_name = 'Sally Smith' # Hard-coded author name.
    648 
    649         # ITEM AUTHOR E-MAIL --One of the following three is optional. The
    650         # framework looks for them in this order.
    651         #
    652         # If you specify this, you must specify item_author_name.
    653 
    654         def item_author_email(self, obj):
    655             """
    656             Takes an item, as returned by items(), and returns the item's
    657             author's e-mail as a normal Python string.
    658             """
    659 
    660         def item_author_email(self):
    661             """
    662             Returns the author e-mail for every item in the feed.
    663             """
    664 
    665         item_author_email = 'test@example.com' # Hard-coded author e-mail.
    666 
    667         # ITEM AUTHOR LINK --One of the following three is optional. The
    668         # framework looks for them in this order. In each case, the URL should
    669         # include the "http://" and domain name.
    670         #
    671         # If you specify this, you must specify item_author_name.
    672 
    673         def item_author_link(self, obj):
    674             """
    675             Takes an item, as returned by items(), and returns the item's
    676             author's URL as a normal Python string.
    677             """
    678 
    679         def item_author_link(self):
    680             """
    681             Returns the author URL for every item in the feed.
    682             """
    683 
    684         item_author_link = 'http://www.example.com/' # Hard-coded author URL.
    685 
    686         # ITEM ENCLOSURE URL -- One of these three is required if you're
    687         # publishing enclosures. The framework looks for them in this order.
    688 
    689         def item_enclosure_url(self, item):
    690             """
    691             Takes an item, as returned by items(), and returns the item's
    692             enclosure URL.
    693             """
    694 
    695         def item_enclosure_url(self):
    696             """
    697             Returns the enclosure URL for every item in the feed.
    698             """
    699 
    700         item_enclosure_url = "/foo/bar.mp3" # Hard-coded enclosure link.
    701 
    702         # ITEM ENCLOSURE LENGTH -- One of these three is required if you're
    703         # publishing enclosures. The framework looks for them in this order.
    704         # In each case, the returned value should be either an integer, or a
    705         # string representation of the integer, in bytes.
    706 
    707         def item_enclosure_length(self, item):
    708             """
    709             Takes an item, as returned by items(), and returns the item's
    710             enclosure length.
    711             """
    712 
    713         def item_enclosure_length(self):
    714             """
    715             Returns the enclosure length for every item in the feed.
    716             """
    717 
    718         item_enclosure_length = 32000 # Hard-coded enclosure length.
    719 
    720         # ITEM ENCLOSURE MIME TYPE -- One of these three is required if you're
    721         # publishing enclosures. The framework looks for them in this order.
    722 
    723         def item_enclosure_mime_type(self, item):
    724             """
    725             Takes an item, as returned by items(), and returns the item's
    726             enclosure MIME type.
    727             """
    728 
    729         def item_enclosure_mime_type(self):
    730             """
    731             Returns the enclosure MIME type for every item in the feed.
    732             """
    733 
    734         item_enclosure_mime_type = "audio/mpeg" # Hard-coded enclosure MIME type.
    735 
    736         # ITEM PUBDATE -- It's optional to use one of these three. This is a
    737         # hook that specifies how to get the pubdate for a given item.
    738         # In each case, the method/attribute should return a Python
    739         # datetime.datetime object.
    740 
    741         def item_pubdate(self, item):
    742             """
    743             Takes an item, as returned by items(), and returns the item's
    744             pubdate.
    745             """
    746 
    747         def item_pubdate(self):
    748             """
    749             Returns the pubdate for every item in the feed.
    750             """
    751 
    752         item_pubdate = datetime.datetime(2005, 5, 3) # Hard-coded pubdate.
    753 
    754         # ITEM CATEGORIES -- It's optional to use one of these three. This is
    755         # a hook that specifies how to get the list of categories for a given
    756         # item. In each case, the method/attribute should return an iterable
    757         # object that returns strings.
    758 
    759         def item_categories(self, item):
    760             """
    761             Takes an item, as returned by items(), and returns the item's
    762             categories.
    763             """
    764 
    765         def item_categories(self):
    766             """
    767             Returns the categories for every item in the feed.
    768             """
    769 
    770         item_categories = ("python", "django") # Hard-coded categories.
    771 
    772         # ITEM COPYRIGHT NOTICE (only applicable to Atom feeds) -- One of the
    773         # following three is optional. The framework looks for them in this
    774         # order.
    775 
    776         def item_copyright(self, obj):
    777             """
    778             Takes an item, as returned by items(), and returns the item's
    779             copyright notice as a normal Python string.
    780             """
    781 
    782         def item_copyright(self):
    783             """
    784             Returns the copyright notice for every item in the feed.
    785             """
    786 
    787         item_copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice.
    788 
    789 
    790 The low-level framework
    791 =======================
    792 
    793 Behind the scenes, the high-level RSS framework uses a lower-level framework
    794 for generating feeds' XML. This framework lives in a single module:
    795 `django/utils/feedgenerator.py`_.
    796 
    797 Feel free to use this framework on your own, for lower-level tasks.
    798 
    799 The ``feedgenerator`` module contains a base class ``SyndicationFeed`` and
    800 several subclasses:
    801 
    802     * ``RssUserland091Feed``
    803     * ``Rss201rev2Feed``
    804     * ``Atom1Feed``
    805 
    806 Each of these three classes knows how to render a certain type of feed as XML.
    807 They share this interface:
    808 
    809 ``__init__(title, link, description, language=None, author_email=None,``
    810 ``author_name=None, author_link=None, subtitle=None, categories=None,``
    811 ``feed_url=None)``
    812 
    813 Initializes the feed with the given metadata, which applies to the entire feed
    814 (i.e., not just to a specific item in the feed).
    815 
    816 All parameters, if given, should be Unicode objects, except ``categories``,
    817 which should be a sequence of Unicode objects.
    818 
    819 ``add_item(title, link, description, author_email=None, author_name=None,``
    820 ``pubdate=None, comments=None, unique_id=None, enclosure=None, categories=())``
    821 
    822 Add an item to the feed with the given parameters. All parameters, if given,
    823 should be Unicode objects, except:
    824 
    825     * ``pubdate`` should be a `Python datetime object`_.
    826     * ``enclosure`` should be an instance of ``feedgenerator.Enclosure``.
    827     * ``categories`` should be a sequence of Unicode objects.
    828 
    829 ``write(outfile, encoding)``
    830 
    831 Outputs the feed in the given encoding to outfile, which is a file-like object.
    832 
    833 ``writeString(encoding)``
    834 
    835 Returns the feed as a string in the given encoding.
    836 
    837 Example usage
    838 -------------
    839 
    840 This example creates an Atom 1.0 feed and prints it to standard output::
    841 
    842     >>> from django.utils import feedgenerator
    843     >>> f = feedgenerator.Atom1Feed(
    844     ...     title=u"My Weblog",
    845     ...     link=u"http://www.example.com/",
    846     ...     description=u"In which I write about what I ate today.",
    847     ...     language=u"en")
    848     >>> f.add_item(title=u"Hot dog today",
    849     ...     link=u"http://www.example.com/entries/1/",
    850     ...     description=u"<p>Today I had a Vienna Beef hot dog. It was pink, plump and perfect.</p>")
    851     >>> print f.writeString('utf8')
    852     <?xml version="1.0" encoding="utf8"?>
    853     <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title>My Weblog</title>
    854     <link href="http://www.example.com/"></link><id>http://www.example.com/</id>
    855     <updated>Sat, 12 Nov 2005 00:28:43 -0000</updated><entry><title>Hot dog today</title>
    856     <link>http://www.example.com/entries/1/</link><id>tag:www.example.com/entries/1/</id>
    857     <summary type="html">&lt;p&gt;Today I had a Vienna Beef hot dog. It was pink, plump and perfect.&lt;/p&gt;</summary>
    858     </entry></feed>
    859 
    860 .. _django/utils/feedgenerator.py: http://code.djangoproject.com/browser/django/trunk/django/utils/feedgenerator.py
    861 .. _Python datetime object: http://www.python.org/doc/current/lib/module-datetime.html
     1==============================
     2The syndication feed framework
     3==============================
     4
     5Django comes with a high-level syndication-feed-generating framework that makes
     6creating RSS_ and Atom_ feeds easy.
     7
     8To create any syndication feed, all you have to do is write a short Python
     9class. You can create as many feeds as you want.
     10
     11Django also comes with a lower-level feed-generating API. Use this if you want
     12to generate feeds outside of a Web context, or in some other lower-level way.
     13
     14.. _RSS: http://www.whatisrss.com/
     15.. _Atom: http://www.atomenabled.org/
     16
     17The high-level framework
     18========================
     19
     20Overview
     21--------
     22
     23The high-level feed-generating framework is a view that's hooked to ``/feeds/``
     24by default. Django uses the remainder of the URL (everything after ``/feeds/``)
     25to determine which feed to output.
     26
     27To create a feed, just write a ``Feed`` class and point to it in your URLconf_.
     28
     29.. _URLconf: ../url_dispatch/
     30
     31Initialization
     32--------------
     33
     34If you're not using the latest Django development version, you'll need to make
     35sure Django's sites framework is installed -- including its database table.
     36(See the `sites framework documentation`_ for more information.) This has
     37changed in the Django development version; the syndication feed framework no
     38longer requires the sites framework.
     39
     40To activate syndication feeds on your Django site, add this line to your
     41URLconf_::
     42
     43    (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}),
     44
     45This tells Django to use the RSS framework to handle all URLs starting with
     46``"feeds/"``. (You can change that ``"feeds/"`` prefix to fit your own needs.)
     47
     48This URLconf line has an extra argument: ``{'feed_dict': feeds}``. Use this
     49extra argument to pass the syndication framework the feeds that should be
     50published under that URL.
     51
     52Specifically, ``feed_dict`` should be a dictionary that maps a feed's slug
     53(short URL label) to its ``Feed`` class.
     54
     55You can define the ``feed_dict`` in the URLconf itself. Here's a full example
     56URLconf::
     57
     58    from django.conf.urls.defaults import *
     59    from myproject.feeds import LatestEntries, LatestEntriesByCategory
     60
     61    feeds = {
     62        'latest': LatestEntries,
     63        'categories': LatestEntriesByCategory,
     64    }
     65
     66    urlpatterns = patterns('',
     67        # ...
     68        (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
     69            {'feed_dict': feeds}),
     70        # ...
     71    )
     72
     73The above example registers two feeds:
     74
     75    * The feed represented by ``LatestEntries`` will live at ``feeds/latest/``.
     76    * The feed represented by ``LatestEntriesByCategory`` will live at
     77      ``feeds/categories/``.
     78
     79Once that's set up, you just need to define the ``Feed`` classes themselves.
     80
     81.. _sites framework documentation: ../sites/
     82.. _URLconf: ../url_dispatch/
     83.. _settings file: ../settings/
     84
     85Feed classes
     86------------
     87
     88A ``Feed`` class is a simple Python class that represents a syndication feed.
     89A feed can be simple (e.g., a "site news" feed, or a basic feed displaying
     90the latest entries of a blog) or more complex (e.g., a feed displaying all the
     91blog entries in a particular category, where the category is variable).
     92
     93``Feed`` classes must subclass ``django.contrib.syndication.feeds.Feed``. They
     94can live anywhere in your codebase.
     95
     96A simple example
     97----------------
     98
     99This simple example, taken from `chicagocrime.org`_, describes a feed of the
     100latest five news items::
     101
     102    from django.contrib.syndication.feeds import Feed
     103    from chicagocrime.models import NewsItem
     104
     105    class LatestEntries(Feed):
     106        title = "Chicagocrime.org site news"
     107        link = "/sitenews/"
     108        description = "Updates on changes and additions to chicagocrime.org."
     109
     110        def items(self):
     111            return NewsItem.objects.order_by('-pub_date')[:5]
     112
     113Note:
     114
     115    * The class subclasses ``django.contrib.syndication.feeds.Feed``.
     116    * ``title``, ``link`` and ``description`` correspond to the standard
     117      RSS ``<title>``, ``<link>`` and ``<description>`` elements, respectively.
     118    * ``items()`` is, simply, a method that returns a list of objects that
     119      should be included in the feed as ``<item>`` elements. Although this
     120      example returns ``NewsItem`` objects using Django's
     121      `object-relational mapper`_, ``items()`` doesn't have to return model
     122      instances. Although you get a few bits of functionality "for free" by
     123      using Django models, ``items()`` can return any type of object you want.
     124    * If you're creating an Atom feed, rather than an RSS feed, set the
     125      ``subtitle`` attribute instead of the ``description`` attribute. See
     126      `Publishing Atom and RSS feeds in tandem`_, later, for an example.
     127
     128One thing's left to do. In an RSS feed, each ``<item>`` has a ``<title>``,
     129``<link>`` and ``<description>``. We need to tell the framework what data to
     130put into those elements.
     131
     132    * To specify the contents of ``<title>`` and ``<description>``, create
     133      `Django templates`_ called ``feeds/latest_title.html`` and
     134      ``feeds/latest_description.html``, where ``latest`` is the ``slug``
     135      specified in the URLconf for the given feed. Note the ``.html`` extension
     136      is required. The RSS system renders that template for each item, passing
     137      it two template context variables:
     138
     139          * ``{{ obj }}`` -- The current object (one of whichever objects you
     140            returned in ``items()``).
     141          * ``{{ site }}`` -- A ``django.contrib.sites.models.Site`` object
     142            representing the current site. This is useful for
     143            ``{{ site.domain }}`` or ``{{ site.name }}``. Note that if you're
     144            using the latest Django development version and do *not* have the
     145            Django sites framework installed, this will be set to a
     146            ``django.contrib.sites.models.RequestSite`` object. See the
     147            `RequestSite section of the sites framework documentation`_ for
     148            more.
     149
     150      If you don't create a template for either the title or description, the
     151      framework will use the template ``"{{ obj }}"`` by default -- that is,
     152      the normal string representation of the object. You can also change the
     153      names of these two templates by specifying ``title_template`` and
     154      ``description_template`` as attributes of your ``Feed`` class.
     155    * To specify the contents of ``<link>``, you have two options. For each
     156      item in ``items()``, Django first tries executing a
     157      ``get_absolute_url()`` method on that object. If that method doesn't
     158      exist, it tries calling a method ``item_link()`` in the ``Feed`` class,
     159      passing it a single parameter, ``item``, which is the object itself.
     160      Both ``get_absolute_url()`` and ``item_link()`` should return the item's
     161      URL as a normal Python string. As with ``get_absolute_url()``, the
     162      result of ``item_link()`` will be included directly in the URL, so you
     163      are responsible for doing all necessary URL quoting and conversion to
     164      ASCII inside the method itself.
     165
     166    * For the LatestEntries example above, we could have very simple feed templates:
     167
     168          * latest_title.html::
     169
     170             {{ obj.title }}
     171
     172          * latest_description.html::
     173
     174             {{ obj.description }}
     175
     176.. _chicagocrime.org: http://www.chicagocrime.org/
     177.. _object-relational mapper: ../db-api/
     178.. _Django templates: ../templates/
     179.. _RequestSite section of the sites framework documentation: ../sites/#requestsite-objects
     180
     181A complex example
     182-----------------
     183
     184The framework also supports more complex feeds, via parameters.
     185
     186For example, `chicagocrime.org`_ offers an RSS feed of recent crimes for every
     187police beat in Chicago. It'd be silly to create a separate ``Feed`` class for
     188each police beat; that would violate the `DRY principle`_ and would couple data
     189to programming logic. Instead, the syndication framework lets you make generic
     190feeds that output items based on information in the feed's URL.
     191
     192On chicagocrime.org, the police-beat feeds are accessible via URLs like this:
     193
     194    * ``/rss/beats/0613/`` -- Returns recent crimes for beat 0613.
     195    * ``/rss/beats/1424/`` -- Returns recent crimes for beat 1424.
     196
     197The slug here is ``"beats"``. The syndication framework sees the extra URL bits
     198after the slug -- ``0613`` and ``1424`` -- and gives you a hook to tell it what
     199those URL bits mean, and how they should influence which items get published in
     200the feed.
     201
     202An example makes this clear. Here's the code for these beat-specific feeds::
     203
     204    from django.contrib.syndication.feeds import FeedDoesNotExist
     205
     206    class BeatFeed(Feed):
     207        def get_object(self, bits):
     208            # In case of "/rss/beats/0613/foo/bar/baz/", or other such clutter,
     209            # check that bits has only one member.
     210            if len(bits) != 1:
     211                raise ObjectDoesNotExist
     212            return Beat.objects.get(beat__exact=bits[0])
     213
     214        def title(self, obj):
     215            return "Chicagocrime.org: Crimes for beat %s" % obj.beat
     216
     217        def link(self, obj):
     218            if not obj:
     219                raise FeedDoesNotExist
     220            return obj.get_absolute_url()
     221
     222        def description(self, obj):
     223            return "Crimes recently reported in police beat %s" % obj.beat
     224
     225        def items(self, obj):
     226            return Crime.objects.filter(beat__id__exact=obj.id).order_by('-crime_date')[:30]
     227
     228Here's the basic algorithm the RSS framework follows, given this class and a
     229request to the URL ``/rss/beats/0613/``:
     230
     231    * The framework gets the URL ``/rss/beats/0613/`` and notices there's
     232      an extra bit of URL after the slug. It splits that remaining string by
     233      the slash character (``"/"``) and calls the ``Feed`` class'
     234      ``get_object()`` method, passing it the bits. In this case, bits is
     235      ``['0613']``. For a request to ``/rss/beats/0613/foo/bar/``, bits would
     236      be ``['0613', 'foo', 'bar']``.
     237
     238    * ``get_object()`` is responsible for retrieving the given beat, from the
     239      given ``bits``. In this case, it uses the Django database API to retrieve
     240      the beat. Note that ``get_object()`` should raise
     241      ``django.core.exceptions.ObjectDoesNotExist`` if given invalid
     242      parameters. There's no ``try``/``except`` around the
     243      ``Beat.objects.get()`` call, because it's not necessary; that function
     244      raises ``Beat.DoesNotExist`` on failure, and ``Beat.DoesNotExist`` is a
     245      subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in
     246      ``get_object()`` tells Django to produce a 404 error for that request.
     247
     248    * To generate the feed's ``<title>``, ``<link>`` and ``<description>``,
     249      Django uses the ``title()``, ``link()`` and ``description()`` methods. In
     250      the previous example, they were simple string class attributes, but this
     251      example illustrates that they can be either strings *or* methods. For
     252      each of ``title``, ``link`` and ``description``, Django follows this
     253      algorithm:
     254
     255          * First, it tries to call a method, passing the ``obj`` argument,
     256            where ``obj`` is the object returned by ``get_object()``.
     257          * Failing that, it tries to call a method with no arguments.
     258          * Failing that, it uses the class attribute.
     259
     260      Inside the ``link()`` method, we handle the possibility that ``obj``
     261      might be ``None``, which can occur when the URL isn't fully specified. In
     262      some cases, you might want to do something else in this case, which would
     263      mean you'd need to check for ``obj`` existing in other methods as well.
     264      (The ``link()`` method is called very early in the feed generation
     265      process, so it's a good place to bail out early.)
     266
     267    * Finally, note that ``items()`` in this example also takes the ``obj``
     268      argument. The algorithm for ``items`` is the same as described in the
     269      previous step -- first, it tries ``items(obj)``, then ``items()``, then
     270      finally an ``items`` class attribute (which should be a list).
     271
     272The ``ExampleFeed`` class below gives full documentation on methods and
     273attributes of ``Feed`` classes.
     274
     275.. _DRY principle: http://c2.com/cgi/wiki?DontRepeatYourself
     276
     277Specifying the type of feed
     278---------------------------
     279
     280By default, feeds produced in this framework use RSS 2.0.
     281
     282To change that, add a ``feed_type`` attribute to your ``Feed`` class, like so::
     283
     284    from django.utils.feedgenerator import Atom1Feed
     285
     286    class MyFeed(Feed):
     287        feed_type = Atom1Feed
     288
     289Note that you set ``feed_type`` to a class object, not an instance.
     290
     291Currently available feed types are:
     292
     293    * ``django.utils.feedgenerator.Rss201rev2Feed`` (RSS 2.01. Default.)
     294    * ``django.utils.feedgenerator.RssUserland091Feed`` (RSS 0.91.)
     295    * ``django.utils.feedgenerator.Atom1Feed`` (Atom 1.0.)
     296
     297Enclosures
     298----------
     299
     300To specify enclosures, such as those used in creating podcast feeds, use the
     301``item_enclosure_url``, ``item_enclosure_length`` and
     302``item_enclosure_mime_type`` hooks. See the ``ExampleFeed`` class below for
     303usage examples.
     304
     305Language
     306--------
     307
     308Feeds created by the syndication framework automatically include the
     309appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This
     310comes directly from your `LANGUAGE_CODE setting`_.
     311
     312.. _LANGUAGE_CODE setting: ../settings/#language-code
     313
     314URLs
     315----
     316
     317The ``link`` method/attribute can return either an absolute URL (e.g.
     318``"/blog/"``) or a URL with the fully-qualified domain and protocol (e.g.
     319``"http://www.example.com/blog/"``). If ``link`` doesn't return the domain,
     320the syndication framework will insert the domain of the current site, according
     321to your `SITE_ID setting`_.
     322
     323Atom feeds require a ``<link rel="self">`` that defines the feed's current
     324location. The syndication framework populates this automatically, using the
     325domain of the current site according to the SITE_ID setting.
     326
     327.. _SITE_ID setting: ../settings/#site-id
     328
     329Publishing Atom and RSS feeds in tandem
     330---------------------------------------
     331
     332Some developers like to make available both Atom *and* RSS versions of their
     333feeds. That's easy to do with Django: Just create a subclass of your ``Feed``
     334class and set the ``feed_type`` to something different. Then update your
     335URLconf to add the extra versions.
     336
     337Here's a full example::
     338
     339    from django.contrib.syndication.feeds import Feed
     340    from chicagocrime.models import NewsItem
     341    from django.utils.feedgenerator import Atom1Feed
     342
     343    class RssSiteNewsFeed(Feed):
     344        title = "Chicagocrime.org site news"
     345        link = "/sitenews/"
     346        description = "Updates on changes and additions to chicagocrime.org."
     347
     348        def items(self):
     349            return NewsItem.objects.order_by('-pub_date')[:5]
     350
     351    class AtomSiteNewsFeed(RssSiteNewsFeed):
     352        feed_type = Atom1Feed
     353        subtitle = RssSiteNewsFeed.description
     354
     355.. Note::
     356    In this example, the RSS feed uses a ``description`` while the Atom feed
     357    uses a ``subtitle``. That's because Atom feeds don't provide for a
     358    feed-level "description," but they *do* provide for a "subtitle."
     359
     360    If you provide a ``description`` in your ``Feed`` class, Django will *not*
     361    automatically put that into the ``subtitle`` element, because a subtitle
     362    and description are not necessarily the same thing. Instead, you should
     363    define a ``subtitle`` attribute.
     364
     365    In the above example, we simply set the Atom feed's ``subtitle`` to the
     366    RSS feed's ``description``, because it's quite short already.
     367
     368And the accompanying URLconf::
     369
     370    from django.conf.urls.defaults import *
     371    from myproject.feeds import RssSiteNewsFeed, AtomSiteNewsFeed
     372
     373    feeds = {
     374        'rss': RssSiteNewsFeed,
     375        'atom': AtomSiteNewsFeed,
     376    }
     377
     378    urlpatterns = patterns('',
     379        # ...
     380        (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
     381            {'feed_dict': feeds}),
     382        # ...
     383    )
     384
     385Feed class reference
     386--------------------
     387
     388This example illustrates all possible attributes and methods for a ``Feed`` class::
     389
     390    from django.contrib.syndication.feeds import Feed
     391    from django.utils import feedgenerator
     392
     393    class ExampleFeed(Feed):
     394
     395        # FEED TYPE -- Optional. This should be a class that subclasses
     396        # django.utils.feedgenerator.SyndicationFeed. This designates which
     397        # type of feed this should be: RSS 2.0, Atom 1.0, etc.
     398        # If you don't specify feed_type, your feed will be RSS 2.0.
     399        # This should be a class, not an instance of the class.
     400
     401        feed_type = feedgenerator.Rss201rev2Feed
     402
     403        # TEMPLATE NAMES -- Optional. These should be strings representing
     404        # names of Django templates that the system should use in rendering the
     405        # title and description of your feed items. Both are optional.
     406        # If you don't specify one, or either, Django will use the template
     407        # 'feeds/SLUG_title.html' and 'feeds/SLUG_description.html', where SLUG
     408        # is the slug you specify in the URL.
     409
     410        title_template = None
     411        description_template = None
     412
     413        # TITLE -- One of the following three is required. The framework looks
     414        # for them in this order.
     415
     416        def title(self, obj):
     417            """
     418            Takes the object returned by get_object() and returns the feed's
     419            title as a normal Python string.
     420            """
     421
     422        def title(self):
     423            """
     424            Returns the feed's title as a normal Python string.
     425            """
     426
     427        title = 'foo' # Hard-coded title.
     428
     429        # LINK -- One of the following three is required. The framework looks
     430        # for them in this order.
     431
     432        def link(self, obj):
     433            """
     434            Takes the object returned by get_object() and returns the feed's
     435            link as a normal Python string.
     436            """
     437
     438        def link(self):
     439            """
     440            Returns the feed's link as a normal Python string.
     441            """
     442
     443        link = '/foo/bar/' # Hard-coded link.
     444
     445        # GUID -- One of the following three is optional. The framework looks
     446        # for them in this order. This property is only used for Atom feeds
     447        # (where it is the feed-level ID element). If not provided, the feed
     448        # link is used as the ID.
     449        #
     450        # (New in Django development version)
     451
     452        def feed_guid(self, obj):
     453            """
     454            Takes the object returned by get_object() and returns the globally
     455            unique ID for the feed as a normal Python string.
     456            """
     457
     458        def feed_guid(self):
     459            """
     460            Returns the feed's globally unique ID as a normal Python string.
     461            """
     462
     463        feed_guid = '/foo/bar/1234' # Hard-coded guid.
     464
     465        # DESCRIPTION -- One of the following three is required. The framework
     466        # looks for them in this order.
     467
     468        def description(self, obj):
     469            """
     470            Takes the object returned by get_object() and returns the feed's
     471            description as a normal Python string.
     472            """
     473
     474        def description(self):
     475            """
     476            Returns the feed's description as a normal Python string.
     477            """
     478
     479        description = 'Foo bar baz.' # Hard-coded description.
     480
     481        # AUTHOR NAME --One of the following three is optional. The framework
     482        # looks for them in this order.
     483
     484        def author_name(self, obj):
     485            """
     486            Takes the object returned by get_object() and returns the feed's
     487            author's name as a normal Python string.
     488            """
     489
     490        def author_name(self):
     491            """
     492            Returns the feed's author's name as a normal Python string.
     493            """
     494
     495        author_name = 'Sally Smith' # Hard-coded author name.
     496
     497        # AUTHOR E-MAIL --One of the following three is optional. The framework
     498        # looks for them in this order.
     499
     500        def author_email(self, obj):
     501            """
     502            Takes the object returned by get_object() and returns the feed's
     503            author's e-mail as a normal Python string.
     504            """
     505
     506        def author_email(self):
     507            """
     508            Returns the feed's author's e-mail as a normal Python string.
     509            """
     510
     511        author_email = 'test@example.com' # Hard-coded author e-mail.
     512
     513        # AUTHOR LINK --One of the following three is optional. The framework
     514        # looks for them in this order. In each case, the URL should include
     515        # the "http://" and domain name.
     516
     517        def author_link(self, obj):
     518            """
     519            Takes the object returned by get_object() and returns the feed's
     520            author's URL as a normal Python string.
     521            """
     522
     523        def author_link(self):
     524            """
     525            Returns the feed's author's URL as a normal Python string.
     526            """
     527
     528        author_link = 'http://www.example.com/' # Hard-coded author URL.
     529
     530        # CATEGORIES -- One of the following three is optional. The framework
     531        # looks for them in this order. In each case, the method/attribute
     532        # should return an iterable object that returns strings.
     533
     534        def categories(self, obj):
     535            """
     536            Takes the object returned by get_object() and returns the feed's
     537            categories as iterable over strings.
     538            """
     539
     540        def categories(self):
     541            """
     542            Returns the feed's categories as iterable over strings.
     543            """
     544
     545        categories = ("python", "django") # Hard-coded list of categories.
     546
     547        # COPYRIGHT NOTICE -- One of the following three is optional. The
     548        # framework looks for them in this order.
     549
     550        def copyright(self, obj):
     551            """
     552            Takes the object returned by get_object() and returns the feed's
     553            copyright notice as a normal Python string.
     554            """
     555
     556        def copyright(self):
     557            """
     558            Returns the feed's copyright notice as a normal Python string.
     559            """
     560
     561        copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice.
     562
     563        # TTL -- One of the following three is optional. The framework looks
     564        # for them in this order. Ignored for Atom feeds.
     565
     566        def ttl(self, obj):
     567            """
     568            Takes the object returned by get_object() and returns the feed's
     569            TTL (Time To Live) as a normal Python string.
     570            """
     571
     572        def ttl(self):
     573            """
     574            Returns the feed's TTL as a normal Python string.
     575            """
     576
     577        ttl = 600 # Hard-coded Time To Live.
     578
     579        # ITEMS -- One of the following three is required. The framework looks
     580        # for them in this order.
     581
     582        def items(self, obj):
     583            """
     584            Takes the object returned by get_object() and returns a list of
     585            items to publish in this feed.
     586            """
     587
     588        def items(self):
     589            """
     590            Returns a list of items to publish in this feed.
     591            """
     592
     593        items = ('Item 1', 'Item 2') # Hard-coded items.
     594
     595        # GET_OBJECT -- This is required for feeds that publish different data
     596        # for different URL parameters. (See "A complex example" above.)
     597
     598        def get_object(self, bits):
     599            """
     600            Takes a list of strings gleaned from the URL and returns an object
     601            represented by this feed. Raises
     602            django.core.exceptions.ObjectDoesNotExist on error.
     603            """
     604
     605        # ITEM LINK -- One of these three is required. The framework looks for
     606        # them in this order.
     607
     608        # First, the framework tries the two methods below, in
     609        # order. Failing that, it falls back to the get_absolute_url()
     610        # method on each item returned by items().
     611
     612        def item_link(self, item):
     613            """
     614            Takes an item, as returned by items(), and returns the item's URL.
     615            """
     616
     617        def item_link(self):
     618            """
     619            Returns the URL for every item in the feed.
     620            """
     621
     622        # ITEM_GUID -- The following method is optional. This property is
     623        # only used for Atom feeds (it is the ID element for an item in an
     624        # Atom feed). If not provided, the item's link is used by default.
     625        #
     626        # (New in Django development version)
     627
     628        def item_guid(self, obj):
     629            """
     630            Takes an item, as return by items(), and returns the item's ID.
     631            """
     632
     633        # ITEM AUTHOR NAME -- One of the following three is optional. The
     634        # framework looks for them in this order.
     635
     636        def item_author_name(self, item):
     637            """
     638            Takes an item, as returned by items(), and returns the item's
     639            author's name as a normal Python string.
     640            """
     641
     642        def item_author_name(self):
     643            """
     644            Returns the author name for every item in the feed.
     645            """
     646
     647        item_author_name = 'Sally Smith' # Hard-coded author name.
     648
     649        # ITEM AUTHOR E-MAIL --One of the following three is optional. The
     650        # framework looks for them in this order.
     651        #
     652        # If you specify this, you must specify item_author_name.
     653
     654        def item_author_email(self, obj):
     655            """
     656            Takes an item, as returned by items(), and returns the item's
     657            author's e-mail as a normal Python string.
     658            """
     659
     660        def item_author_email(self):
     661            """
     662            Returns the author e-mail for every item in the feed.
     663            """
     664
     665        item_author_email = 'test@example.com' # Hard-coded author e-mail.
     666
     667        # ITEM AUTHOR LINK --One of the following three is optional. The
     668        # framework looks for them in this order. In each case, the URL should
     669        # include the "http://" and domain name.
     670        #
     671        # If you specify this, you must specify item_author_name.
     672
     673        def item_author_link(self, obj):
     674            """
     675            Takes an item, as returned by items(), and returns the item's
     676            author's URL as a normal Python string.
     677            """
     678
     679        def item_author_link(self):
     680            """
     681            Returns the author URL for every item in the feed.
     682            """
     683
     684        item_author_link = 'http://www.example.com/' # Hard-coded author URL.
     685
     686        # ITEM ENCLOSURE URL -- One of these three is required if you're
     687        # publishing enclosures. The framework looks for them in this order.
     688
     689        def item_enclosure_url(self, item):
     690            """
     691            Takes an item, as returned by items(), and returns the item's
     692            enclosure URL.
     693            """
     694
     695        def item_enclosure_url(self):
     696            """
     697            Returns the enclosure URL for every item in the feed.
     698            """
     699
     700        item_enclosure_url = "/foo/bar.mp3" # Hard-coded enclosure link.
     701
     702        # ITEM ENCLOSURE LENGTH -- One of these three is required if you're
     703        # publishing enclosures. The framework looks for them in this order.
     704        # In each case, the returned value should be either an integer, or a
     705        # string representation of the integer, in bytes.
     706
     707        def item_enclosure_length(self, item):
     708            """
     709            Takes an item, as returned by items(), and returns the item's
     710            enclosure length.
     711            """
     712
     713        def item_enclosure_length(self):
     714            """
     715            Returns the enclosure length for every item in the feed.
     716            """
     717
     718        item_enclosure_length = 32000 # Hard-coded enclosure length.
     719
     720        # ITEM ENCLOSURE MIME TYPE -- One of these three is required if you're
     721        # publishing enclosures. The framework looks for them in this order.
     722
     723        def item_enclosure_mime_type(self, item):
     724            """
     725            Takes an item, as returned by items(), and returns the item's
     726            enclosure MIME type.
     727            """
     728
     729        def item_enclosure_mime_type(self):
     730            """
     731            Returns the enclosure MIME type for every item in the feed.
     732            """
     733
     734        item_enclosure_mime_type = "audio/mpeg" # Hard-coded enclosure MIME type.
     735
     736        # ITEM PUBDATE -- It's optional to use one of these three. This is a
     737        # hook that specifies how to get the pubdate for a given item.
     738        # In each case, the method/attribute should return a Python
     739        # datetime.datetime object.
     740
     741        def item_pubdate(self, item):
     742            """
     743            Takes an item, as returned by items(), and returns the item's
     744            pubdate.
     745            """
     746
     747        def item_pubdate(self):
     748            """
     749            Returns the pubdate for every item in the feed.
     750            """
     751
     752        item_pubdate = datetime.datetime(2005, 5, 3) # Hard-coded pubdate.
     753
     754        # ITEM CATEGORIES -- It's optional to use one of these three. This is
     755        # a hook that specifies how to get the list of categories for a given
     756        # item. In each case, the method/attribute should return an iterable
     757        # object that returns strings.
     758
     759        def item_categories(self, item):
     760            """
     761            Takes an item, as returned by items(), and returns the item's
     762            categories.
     763            """
     764
     765        def item_categories(self):
     766            """
     767            Returns the categories for every item in the feed.
     768            """
     769
     770        item_categories = ("python", "django") # Hard-coded categories.
     771
     772        # ITEM COPYRIGHT NOTICE (only applicable to Atom feeds) -- One of the
     773        # following three is optional. The framework looks for them in this
     774        # order.
     775
     776        def item_copyright(self, obj):
     777            """
     778            Takes an item, as returned by items(), and returns the item's
     779            copyright notice as a normal Python string.
     780            """
     781
     782        def item_copyright(self):
     783            """
     784            Returns the copyright notice for every item in the feed.
     785            """
     786
     787        item_copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice.
     788
     789
     790The low-level framework
     791=======================
     792
     793Behind the scenes, the high-level RSS framework uses a lower-level framework
     794for generating feeds' XML. This framework lives in a single module:
     795`django/utils/feedgenerator.py`_.
     796
     797Feel free to use this framework on your own, for lower-level tasks.
     798
     799The ``feedgenerator`` module contains a base class ``SyndicationFeed`` and
     800several subclasses:
     801
     802    * ``RssUserland091Feed``
     803    * ``Rss201rev2Feed``
     804    * ``Atom1Feed``
     805
     806Each of these three classes knows how to render a certain type of feed as XML.
     807They share this interface:
     808
     809``__init__(title, link, description, language=None, author_email=None,``
     810``author_name=None, author_link=None, subtitle=None, categories=None,``
     811``feed_url=None)``
     812
     813Initializes the feed with the given metadata, which applies to the entire feed
     814(i.e., not just to a specific item in the feed).
     815
     816All parameters, if given, should be Unicode objects, except ``categories``,
     817which should be a sequence of Unicode objects.
     818
     819``add_item(title, link, description, author_email=None, author_name=None,``
     820``pubdate=None, comments=None, unique_id=None, enclosure=None, categories=())``
     821
     822Add an item to the feed with the given parameters. All parameters, if given,
     823should be Unicode objects, except:
     824
     825    * ``pubdate`` should be a `Python datetime object`_.
     826    * ``enclosure`` should be an instance of ``feedgenerator.Enclosure``.
     827    * ``categories`` should be a sequence of Unicode objects.
     828
     829``write(outfile, encoding)``
     830
     831Outputs the feed in the given encoding to outfile, which is a file-like object.
     832
     833``writeString(encoding)``
     834
     835Returns the feed as a string in the given encoding.
     836
     837Example usage
     838-------------
     839
     840This example creates an Atom 1.0 feed and prints it to standard output::
     841
     842    >>> from django.utils import feedgenerator
     843    >>> from datetime import datetime
     844    >>> f = feedgenerator.Atom1Feed(
     845    ...     title=u"My Weblog",
     846    ...     link=u"http://www.example.com/",
     847    ...     description=u"In which I write about what I ate today.",
     848    ...     language=u"en",
     849    ...     author_name=u"Myself",
     850    ...     feed_url=u"http://www.example.com/atom.xml")
     851    >>> f.add_item(title=u"Hot dog today",
     852    ...     link=u"http://www.example.com/entries/1/",
     853    ...     pubdate=datetime.now(),
     854    ...     description=u"<p>Today I had a Vienna Beef hot dog. It was pink, plump and perfect.</p>")
     855    >>> print f.writeString('utf-8')
     856    <?xml version="1.0" encoding="utf-8"?>
     857    <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title>My Weblog</title><link href="http://www.example
     858    .com/" rel="alternate"></link><link href="http://www.example.com/atom.xml" rel="self"></link><id>http://www.ex
     859    ample.com/</id><updated>2008-02-18T16:04:39Z</updated><author><name>Myself</name></author><entry><title>Hot do
     860    g today</title><link href="http://www.example.com/entries/1/" rel="alternate"></link><updated>2008-02-18T16:04
     861    :39Z</updated><id>tag:www.example.com,2008-02-18:/entries/1/</id><summary type="html">&lt;p&gt;Today I had a V
     862    ienna Beef hot dog. It was pink, plump and perfect.&lt;/p&gt;</summary></entry></feed>
     863    >>>
     864
     865This passes the `W3C Feed Validation Service`_.
     866
     867.. _W3C Feed Validation Service: http://validator.w3.org/feed
     868.. _django/utils/feedgenerator.py: http://code.djangoproject.com/browser/django/trunk/django/utils/feedgenerator.py
     869.. _Python datetime object: http://www.python.org/doc/current/lib/module-datetime.html
Back to Top