Django

Code

Ticket #6039: 6039.patch

File 6039.patch, 67.2 kB (added by stugots, 5 months ago)

Patch for this ticket.

  • docs/syndication_feeds.txt

    old new  
    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&nbs