| ============================== |
| The syndication feed framework |
| ============================== |
| |
| .. module:: django.contrib.syndication |
| :synopsis: A framework for generating syndication feeds, in RSS and Atom, |
| quite easily. |
| |
| Django comes with a high-level syndication-feed-generating framework |
| that makes creating RSS_ and Atom_ feeds easy. |
| |
| To create any syndication feed, all you have to do is write a short |
| Python class. You can create as many feeds as you want. |
| |
| Django also comes with a lower-level feed-generating API. Use this if |
| you want to generate feeds outside of a Web context, or in some other |
| lower-level way. |
| |
| .. _RSS: http://www.whatisrss.com/ |
| .. _Atom: http://tools.ietf.org/html/rfc4287 |
| |
| The high-level framework |
| ======================== |
| |
| Overview |
| -------- |
| |
| The high-level feed-generating framework is supplied by the |
| :class:`~django.contrib.syndication.views.Feed` class. To create a |
| feed, write a :class:`~django.contrib.syndication.views.Feed` class |
| and point to an instance of it in your :doc:`URLconf |
| </topics/http/urls>`. |
| |
| Feed classes |
| ------------ |
| |
| A :class:`~django.contrib.syndication.views.Feed` class is a Python |
| class that represents a syndication feed. A feed can be simple (e.g., |
| a "site news" feed, or a basic feed displaying the latest entries of a |
| blog) or more complex (e.g., a feed displaying all the blog entries in |
| a particular category, where the category is variable). |
| |
| Feed classes subclass :class:`django.contrib.syndication.views.Feed`. |
| They can live anywhere in your codebase. |
| |
| Instances of :class:`~django.contrib.syndication.views.Feed` classes |
| are views which can be used in your :doc:`URLconf </topics/http/urls>`. |
| |
| A simple example |
| ---------------- |
| |
| This simple example, taken from a hypothetical police beat news site describes |
| a feed of the latest five news items:: |
| |
| from django.contrib.syndication.views import Feed |
| from django.core.urlresolvers import reverse |
| from policebeat.models import NewsItem |
| |
| class LatestEntriesFeed(Feed): |
| title = "Police beat site news" |
| link = "/sitenews/" |
| description = "Updates on changes and additions to police beat central." |
| |
| def items(self): |
| return NewsItem.objects.order_by('-pub_date')[:5] |
| |
| def item_title(self, item): |
| return item.title |
| |
| def item_description(self, item): |
| return item.description |
| |
| # item_link is only needed if NewsItem has no get_absolute_url method. |
| def item_link(self, item): |
| return reverse('news-item', args=[item.pk]) |
| |
| To connect a URL to this feed, put an instance of the Feed object in |
| your :doc:`URLconf </topics/http/urls>`. For example:: |
| |
| from django.conf.urls import patterns |
| from myproject.feeds import LatestEntriesFeed |
| |
| urlpatterns = patterns('', |
| # ... |
| (r'^latest/feed/$', LatestEntriesFeed()), |
| # ... |
| ) |
| |
| Note: |
| |
| * The Feed class subclasses :class:`django.contrib.syndication.views.Feed`. |
| |
| * ``title``, ``link`` and ``description`` correspond to the |
| standard RSS ``<title>``, ``<link>`` and ``<description>`` elements, |
| respectively. |
| |
| * ``items()`` is, simply, a method that returns a list of objects that |
| should be included in the feed as ``<item>`` elements. Although this |
| example returns ``NewsItem`` objects using Django's |
| :doc:`object-relational mapper </ref/models/querysets>`, ``items()`` |
| doesn't have to return model instances. Although you get a few bits of |
| functionality "for free" by using Django models, ``items()`` can |
| return any type of object you want. |
| |
| * If you're creating an Atom feed, rather than an RSS feed, set the |
| ``subtitle`` attribute instead of the ``description`` attribute. |
| See `Publishing Atom and RSS feeds in tandem`_, later, for an example. |
| |
| One thing is left to do. In an RSS feed, each ``<item>`` has a ``<title>``, |
| ``<link>`` and ``<description>``. We need to tell the framework what data to put |
| into those elements. |
| |
| * For the contents of ``<title>`` and ``<description>``, Django tries |
| calling the methods ``item_title()`` and ``item_description()`` on |
| the :class:`~django.contrib.syndication.views.Feed` class. They are passed |
| a single parameter, ``item``, which is the object itself. These are |
| optional; by default, the unicode representation of the object is used for |
| both. |
| |
| If you want to do any special formatting for either the title or |
| description, :doc:`Django templates </topics/templates>` can be used |
| instead. Their paths can be specified with the ``title_template`` and |
| ``description_template`` attributes on the |
| :class:`~django.contrib.syndication.views.Feed` class. The templates are |
| rendered for each item and are passed two template context variables: |
| |
| * ``{{ obj }}`` -- The current object (one of whichever objects you |
| returned in ``items()``). |
| |
| * ``{{ site }}`` -- A :class:`django.contrib.sites.models.Site` object |
| representing the current site. This is useful for ``{{ site.domain |
| }}`` or ``{{ site.name }}``. If you do *not* have the Django sites |
| framework installed, this will be set to a |
| :class:`django.contrib.sites.models.RequestSite` object. See the |
| :ref:`RequestSite section of the sites framework documentation |
| <requestsite-objects>` for more. |
| |
| See `a complex example`_ below that uses a description template. |
| |
| * To specify the contents of ``<link>``, you have two options. For each item |
| in ``items()``, Django first tries calling the |
| ``item_link()`` method on the |
| :class:`~django.contrib.syndication.views.Feed` class. In a similar way to |
| the title and description, it is passed it a single parameter, |
| ``item``. If that method doesn't exist, Django tries executing a |
| ``get_absolute_url()`` method on that object. Both |
| ``get_absolute_url()`` and ``item_link()`` should return the |
| item's URL as a normal Python string. As with ``get_absolute_url()``, the |
| result of ``item_link()`` will be included directly in the URL, so you |
| are responsible for doing all necessary URL quoting and conversion to |
| ASCII inside the method itself. |
| |
| A complex example |
| ----------------- |
| |
| The framework also supports more complex feeds, via arguments. |
| |
| For example, a website could offer an RSS feed of recent crimes for every |
| police beat in a city. It'd be silly to create a separate |
| :class:`~django.contrib.syndication.views.Feed` class for each police beat; that |
| would violate the :ref:`DRY principle <dry>` and would couple data to |
| programming logic. Instead, the syndication framework lets you access the |
| arguments passed from your :doc:`URLconf </topics/http/urls>` so feeds can output |
| items based on information in the feed's URL. |
| |
| The police beat feeds could be accessible via URLs like this: |
| |
| * :file:`/beats/613/rss/` -- Returns recent crimes for beat 613. |
| * :file:`/beats/1424/rss/` -- Returns recent crimes for beat 1424. |
| |
| These can be matched with a :doc:`URLconf </topics/http/urls>` line such as:: |
| |
| (r'^beats/(?P<beat_id>\d+)/rss/$', BeatFeed()), |
| |
| Like a view, the arguments in the URL are passed to the ``get_object()`` |
| method along with the request object. |
| |
| Here's the code for these beat-specific feeds:: |
| |
| from django.contrib.syndication.views import FeedDoesNotExist |
| from django.shortcuts import get_object_or_404 |
| |
| class BeatFeed(Feed): |
| description_template = 'feeds/beat_description.html' |
| |
| def get_object(self, request, beat_id): |
| return get_object_or_404(Beat, pk=beat_id) |
| |
| def title(self, obj): |
| return "Police beat central: Crimes for beat %s" % obj.beat |
| |
| def link(self, obj): |
| return obj.get_absolute_url() |
| |
| def description(self, obj): |
| return "Crimes recently reported in police beat %s" % obj.beat |
| |
| def items(self, obj): |
| return Crime.objects.filter(beat=obj).order_by('-crime_date')[:30] |
| |
| To generate the feed's ``<title>``, ``<link>`` and ``<description>``, Django |
| uses the ``title()``, ``link()`` and ``description()`` methods. In |
| the previous example, they were simple string class attributes, but this example |
| illustrates that they can be either strings *or* methods. For each of |
| ``title``, ``link`` and ``description``, Django follows this |
| algorithm: |
| |
| * First, it tries to call a method, passing the ``obj`` argument, where |
| ``obj`` is the object returned by ``get_object()``. |
| |
| * Failing that, it tries to call a method with no arguments. |
| |
| * Failing that, it uses the class attribute. |
| |
| Also note that ``items()`` also follows the same algorithm -- first, it |
| tries ``items(obj)``, then ``items()``, then finally an ``items`` |
| class attribute (which should be a list). |
| |
| We are using a template for the item descriptions. It can be very simple: |
| |
| .. code-block:: html+django |
| |
| {{ obj.description }} |
| |
| However, you are free to add formatting as desired. |
| |
| The ``ExampleFeed`` class below gives full documentation on methods and |
| attributes of :class:`~django.contrib.syndication.views.Feed` classes. |
| |
| Specifying the type of feed |
| --------------------------- |
| |
| By default, feeds produced in this framework use RSS 2.0. |
| |
| To change that, add a ``feed_type`` attribute to your |
| :class:`~django.contrib.syndication.views.Feed` class, like so:: |
| |
| from django.utils.feedgenerator import Atom1Feed |
| |
| class MyFeed(Feed): |
| feed_type = Atom1Feed |
| |
| Note that you set ``feed_type`` to a class object, not an instance. |
| |
| Currently available feed types are: |
| |
| * :class:`django.utils.feedgenerator.Rss201rev2Feed` (RSS 2.01. Default.) |
| * :class:`django.utils.feedgenerator.RssUserland091Feed` (RSS 0.91.) |
| * :class:`django.utils.feedgenerator.Atom1Feed` (Atom 1.0.) |
| |
| Enclosures |
| ---------- |
| |
| To specify enclosures, such as those used in creating podcast feeds, use the |
| ``item_enclosure_url``, ``item_enclosure_length`` and |
| ``item_enclosure_mime_type`` hooks. See the ``ExampleFeed`` class below for |
| usage examples. |
| |
| Language |
| -------- |
| |
| Feeds created by the syndication framework automatically include the |
| appropriate ``<language>`` tag (RSS 2.0) or ``xml:lang`` attribute (Atom). This |
| comes directly from your :setting:`LANGUAGE_CODE` setting. |
| |
| URLs |
| ---- |
| |
| The ``link`` method/attribute can return either an absolute path (e.g. |
| :file:`"/blog/"`) or a URL with the fully-qualified domain and protocol (e.g. |
| ``"http://www.example.com/blog/"``). If ``link`` doesn't return the domain, |
| the syndication framework will insert the domain of the current site, according |
| to your :setting:`SITE_ID setting <SITE_ID>`. |
| |
| Atom feeds require a ``<link rel="self">`` that defines the feed's current |
| location. The syndication framework populates this automatically, using the |
| domain of the current site according to the :setting:`SITE_ID` setting. |
| |
| Publishing Atom and RSS feeds in tandem |
| --------------------------------------- |
| |
| Some developers like to make available both Atom *and* RSS versions of their |
| feeds. That's easy to do with Django: Just create a subclass of your |
| :class:`~django.contrib.syndication.views.Feed` |
| class and set the ``feed_type`` to something different. Then update your |
| URLconf to add the extra versions. |
| |
| Here's a full example:: |
| |
| from django.contrib.syndication.views import Feed |
| from policebeat.models import NewsItem |
| from django.utils.feedgenerator import Atom1Feed |
| |
| class RssSiteNewsFeed(Feed): |
| title = "Police beat site news" |
| link = "/sitenews/" |
| description = "Updates on changes and additions to police beat central." |
| |
| def items(self): |
| return NewsItem.objects.order_by('-pub_date')[:5] |
| |
| class AtomSiteNewsFeed(RssSiteNewsFeed): |
| feed_type = Atom1Feed |
| subtitle = RssSiteNewsFeed.description |
| |
| .. Note:: |
| In this example, the RSS feed uses a ``description`` while the Atom |
| feed uses a ``subtitle``. That's because Atom feeds don't provide for |
| a feed-level "description," but they *do* provide for a "subtitle." |
| |
| If you provide a ``description`` in your |
| :class:`~django.contrib.syndication.views.Feed` class, Django will *not* |
| automatically put that into the ``subtitle`` element, because a |
| subtitle and description are not necessarily the same thing. Instead, you |
| should define a ``subtitle`` attribute. |
| |
| In the above example, we simply set the Atom feed's ``subtitle`` to the |
| RSS feed's ``description``, because it's quite short already. |
| |
| And the accompanying URLconf:: |
| |
| from django.conf.urls import patterns |
| from myproject.feeds import RssSiteNewsFeed, AtomSiteNewsFeed |
| |
| urlpatterns = patterns('', |
| # ... |
| (r'^sitenews/rss/$', RssSiteNewsFeed()), |
| (r'^sitenews/atom/$', AtomSiteNewsFeed()), |
| # ... |
| ) |
| |
| Feed class reference |
| -------------------- |
| |
| .. class:: views.Feed |
| |
| This example illustrates all possible attributes and methods for a |
| :class:`~django.contrib.syndication.views.Feed` class:: |
| |
| from django.contrib.syndication.views import Feed |
| from django.utils import feedgenerator |
| |
| class ExampleFeed(Feed): |
| |
| # FEED TYPE -- Optional. This should be a class that subclasses |
| # django.utils.feedgenerator.SyndicationFeed. This designates |
| # which type of feed this should be: RSS 2.0, Atom 1.0, etc. If |
| # you don't specify feed_type, your feed will be RSS 2.0. This |
| # should be a class, not an instance of the class. |
| |
| feed_type = feedgenerator.Rss201rev2Feed |
| |
| # TEMPLATE NAMES -- Optional. These should be strings |
| # representing names of Django templates that the system should |
| # use in rendering the title and description of your feed items. |
| # Both are optional. If a template is not specified, the |
| # item_title() or item_description() methods are used instead. |
| |
| title_template = None |
| description_template = None |
| |
| # TITLE -- One of the following three is required. The framework |
| # looks for them in this order. |
| |
| def title(self, obj): |
| """ |
| Takes the object returned by get_object() and returns the |
| feed's title as a normal Python string. |
| """ |
| |
| def title(self): |
| """ |
| Returns the feed's title as a normal Python string. |
| """ |
| |
| title = 'foo' # Hard-coded title. |
| |
| # LINK -- One of the following three is required. The framework |
| # looks for them in this order. |
| |
| def link(self, obj): |
| """ |
| # Takes the object returned by get_object() and returns the URL |
| # of the HTML version of the feed as a normal Python string. |
| """ |
| |
| def link(self): |
| """ |
| Returns the URL of the HTML version of the feed as a normal Python |
| string. |
| """ |
| |
| link = '/blog/' # Hard-coded URL. |
| |
| # FEED_URL -- One of the following three is optional. The framework |
| # looks for them in this order. |
| |
| def feed_url(self, obj): |
| """ |
| # Takes the object returned by get_object() and returns the feed's |
| # own URL as a normal Python string. |
| """ |
| |
| def feed_url(self): |
| """ |
| Returns the feed's own URL as a normal Python string. |
| """ |
| |
| feed_url = '/blog/rss/' # Hard-coded URL. |
| |
| # GUID -- One of the following three is optional. The framework looks |
| # for them in this order. This property is only used for Atom feeds |
| # (where it is the feed-level ID element). If not provided, the feed |
| # link is used as the ID. |
| |
| def feed_guid(self, obj): |
| """ |
| Takes the object returned by get_object() and returns the globally |
| unique ID for the feed as a normal Python string. |
| """ |
| |
| def feed_guid(self): |
| """ |
| Returns the feed's globally unique ID as a normal Python string. |
| """ |
| |
| feed_guid = '/foo/bar/1234' # Hard-coded guid. |
| |
| # DESCRIPTION -- One of the following three is required. The framework |
| # looks for them in this order. |
| |
| def description(self, obj): |
| """ |
| Takes the object returned by get_object() and returns the feed's |
| description as a normal Python string. |
| """ |
| |
| def description(self): |
| """ |
| Returns the feed's description as a normal Python string. |
| """ |
| |
| description = 'Foo bar baz.' # Hard-coded description. |
| |
| # AUTHOR NAME --One of the following three is optional. The framework |
| # looks for them in this order. |
| |
| def author_name(self, obj): |
| """ |
| Takes the object returned by get_object() and returns the feed's |
| author's name as a normal Python string. |
| """ |
| |
| def author_name(self): |
| """ |
| Returns the feed's author's name as a normal Python string. |
| """ |
| |
| author_name = 'Sally Smith' # Hard-coded author name. |
| |
| # AUTHOR EMAIL --One of the following three is optional. The framework |
| # looks for them in this order. |
| |
| def author_email(self, obj): |
| """ |
| Takes the object returned by get_object() and returns the feed's |
| author's email as a normal Python string. |
| """ |
| |
| def author_email(self): |
| """ |
| Returns the feed's author's email as a normal Python string. |
| """ |
| |
| author_email = 'test@example.com' # Hard-coded author email. |
| |
| # AUTHOR LINK --One of the following three is optional. The framework |
| # looks for them in this order. In each case, the URL should include |
| # the "http://" and domain name. |
| |
| def author_link(self, obj): |
| """ |
| Takes the object returned by get_object() and returns the feed's |
| author's URL as a normal Python string. |
| """ |
| |
| def author_link(self): |
| """ |
| Returns the feed's author's URL as a normal Python string. |
| """ |
| |
| author_link = 'http://www.example.com/' # Hard-coded author URL. |
| |
| # CATEGORIES -- One of the following three is optional. The framework |
| # looks for them in this order. In each case, the method/attribute |
| # should return an iterable object that returns strings. |
| |
| def categories(self, obj): |
| """ |
| Takes the object returned by get_object() and returns the feed's |
| categories as iterable over strings. |
| """ |
| |
| def categories(self): |
| """ |
| Returns the feed's categories as iterable over strings. |
| """ |
| |
| categories = ("python", "django") # Hard-coded list of categories. |
| |
| # COPYRIGHT NOTICE -- One of the following three is optional. The |
| # framework looks for them in this order. |
| |
| def feed_copyright(self, obj): |
| """ |
| Takes the object returned by get_object() and returns the feed's |
| copyright notice as a normal Python string. |
| """ |
| |
| def feed_copyright(self): |
| """ |
| Returns the feed's copyright notice as a normal Python string. |
| """ |
| |
| feed_copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice. |
| |
| # TTL -- One of the following three is optional. The framework looks |
| # for them in this order. Ignored for Atom feeds. |
| |
| def ttl(self, obj): |
| """ |
| Takes the object returned by get_object() and returns the feed's |
| TTL (Time To Live) as a normal Python string. |
| """ |
| |
| def ttl(self): |
| """ |
| Returns the feed's TTL as a normal Python string. |
| """ |
| |
| ttl = 600 # Hard-coded Time To Live. |
| |
| # ITEMS -- One of the following three is required. The framework looks |
| # for them in this order. |
| |
| def items(self, obj): |
| """ |
| Takes the object returned by get_object() and returns a list of |
| items to publish in this feed. |
| """ |
| |
| def items(self): |
| """ |
| Returns a list of items to publish in this feed. |
| """ |
| |
| items = ('Item 1', 'Item 2') # Hard-coded items. |
| |
| # GET_OBJECT -- This is required for feeds that publish different data |
| # for different URL parameters. (See "A complex example" above.) |
| |
| def get_object(self, request, *args, **kwargs): |
| """ |
| Takes the current request and the arguments from the URL, and |
| returns an object represented by this feed. Raises |
| django.core.exceptions.ObjectDoesNotExist on error. |
| """ |
| |
| # ITEM TITLE AND DESCRIPTION -- If title_template or |
| # description_template are not defined, these are used instead. Both are |
| # optional, by default they will use the unicode representation of the |
| # item. |
| |
| def item_title(self, item): |
| """ |
| Takes an item, as returned by items(), and returns the item's |
| title as a normal Python string. |
| """ |
| |
| def item_title(self): |
| """ |
| Returns the title for every item in the feed. |
| """ |
| |
| item_title = 'Breaking News: Nothing Happening' # Hard-coded title. |
| |
| def item_description(self, item): |
| """ |
| Takes an item, as returned by items(), and returns the item's |
| description as a normal Python string. |
| """ |
| |
| def item_description(self): |
| """ |
| Returns the description for every item in the feed. |
| """ |
| |
| item_description = 'A description of the item.' # Hard-coded description. |
| |
| # ITEM LINK -- One of these three is required. The framework looks for |
| # them in this order. |
| |
| # First, the framework tries the two methods below, in |
| # order. Failing that, it falls back to the get_absolute_url() |
| # method on each item returned by items(). |
| |
| def item_link(self, item): |
| """ |
| Takes an item, as returned by items(), and returns the item's URL. |
| """ |
| |
| def item_link(self): |
| """ |
| Returns the URL for every item in the feed. |
| """ |
| |
| # ITEM_GUID -- The following method is optional. If not provided, the |
| # item's link is used by default. |
| |
| def item_guid(self, obj): |
| """ |
| Takes an item, as return by items(), and returns the item's ID. |
| """ |
| |
| # ITEM AUTHOR NAME -- One of the following three is optional. The |
| # framework looks for them in this order. |
| |
| def item_author_name(self, item): |
| """ |
| Takes an item, as returned by items(), and returns the item's |
| author's name as a normal Python string. |
| """ |
| |
| def item_author_name(self): |
| """ |
| Returns the author name for every item in the feed. |
| """ |
| |
| item_author_name = 'Sally Smith' # Hard-coded author name. |
| |
| # ITEM AUTHOR EMAIL --One of the following three is optional. The |
| # framework looks for them in this order. |
| # |
| # If you specify this, you must specify item_author_name. |
| |
| def item_author_email(self, obj): |
| """ |
| Takes an item, as returned by items(), and returns the item's |
| author's email as a normal Python string. |
| """ |
| |
| def item_author_email(self): |
| """ |
| Returns the author email for every item in the feed. |
| """ |
| |
| item_author_email = 'test@example.com' # Hard-coded author email. |
| |
| # ITEM AUTHOR LINK -- One of the following three is optional. The |
| # framework looks for them in this order. In each case, the URL should |
| # include the "http://" and domain name. |
| # |
| # If you specify this, you must specify item_author_name. |
| |
| def item_author_link(self, obj): |
| """ |
| Takes an item, as returned by items(), and returns the item's |
| author's URL as a normal Python string. |
| """ |
| |
| def item_author_link(self): |
| """ |
| Returns the author URL for every item in the feed. |
| """ |
| |
| item_author_link = 'http://www.example.com/' # Hard-coded author URL. |
| |
| # ITEM ENCLOSURE URL -- One of these three is required if you're |
| # publishing enclosures. The framework looks for them in this order. |
| |
| def item_enclosure_url(self, item): |
| """ |
| Takes an item, as returned by items(), and returns the item's |
| enclosure URL. |
| """ |
| |
| def item_enclosure_url(self): |
| """ |
| Returns the enclosure URL for every item in the feed. |
| """ |
| |
| item_enclosure_url = "/foo/bar.mp3" # Hard-coded enclosure link. |
| |
| # ITEM ENCLOSURE LENGTH -- One of these three is required if you're |
| # publishing enclosures. The framework looks for them in this order. |
| # In each case, the returned value should be either an integer, or a |
| # string representation of the integer, in bytes. |
| |
| def item_enclosure_length(self, item): |
| """ |
| Takes an item, as returned by items(), and returns the item's |
| enclosure length. |
| """ |
| |
| def item_enclosure_length(self): |
| """ |
| Returns the enclosure length for every item in the feed. |
| """ |
| |
| item_enclosure_length = 32000 # Hard-coded enclosure length. |
| |
| # ITEM ENCLOSURE MIME TYPE -- One of these three is required if you're |
| # publishing enclosures. The framework looks for them in this order. |
| |
| def item_enclosure_mime_type(self, item): |
| """ |
| Takes an item, as returned by items(), and returns the item's |
| enclosure MIME type. |
| """ |
| |
| def item_enclosure_mime_type(self): |
| """ |
| Returns the enclosure MIME type for every item in the feed. |
| """ |
| |
| item_enclosure_mime_type = "audio/mpeg" # Hard-coded enclosure MIME type. |
| |
| # ITEM PUBDATE -- It's optional to use one of these three. This is a |
| # hook that specifies how to get the pubdate for a given item. |
| # In each case, the method/attribute should return a Python |
| # datetime.datetime object. |
| |
| def item_pubdate(self, item): |
| """ |
| Takes an item, as returned by items(), and returns the item's |
| pubdate. |
| """ |
| |
| def item_pubdate(self): |
| """ |
| Returns the pubdate for every item in the feed. |
| """ |
| |
| item_pubdate = datetime.datetime(2005, 5, 3) # Hard-coded pubdate. |
| |
| # ITEM CATEGORIES -- It's optional to use one of these three. This is |
| # a hook that specifies how to get the list of categories for a given |
| # item. In each case, the method/attribute should return an iterable |
| # object that returns strings. |
| |
| def item_categories(self, item): |
| """ |
| Takes an item, as returned by items(), and returns the item's |
| categories. |
| """ |
| |
| def item_categories(self): |
| """ |
| Returns the categories for every item in the feed. |
| """ |
| |
| item_categories = ("python", "django") # Hard-coded categories. |
| |
| # ITEM COPYRIGHT NOTICE (only applicable to Atom feeds) -- One of the |
| # following three is optional. The framework looks for them in this |
| # order. |
| |
| def item_copyright(self, obj): |
| """ |
| Takes an item, as returned by items(), and returns the item's |
| copyright notice as a normal Python string. |
| """ |
| |
| def item_copyright(self): |
| """ |
| Returns the copyright notice for every item in the feed. |
| """ |
| |
| item_copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice. |
| |
| |
| The low-level framework |
| ======================= |
| |
| Behind the scenes, the high-level RSS framework uses a lower-level framework |
| for generating feeds' XML. This framework lives in a single module: |
| `django/utils/feedgenerator.py`_. |
| |
| You use this framework on your own, for lower-level feed generation. You can |
| also create custom feed generator subclasses for use with the ``feed_type`` |
| ``Feed`` option. |
| |
| .. currentmodule:: django.utils.feedgenerator |
| |
| ``SyndicationFeed`` classes |
| --------------------------- |
| |
| The :mod:`~django.utils.feedgenerator` module contains a base class: |
| |
| * :class:`django.utils.feedgenerator.SyndicationFeed` |
| |
| and several subclasses: |
| |
| * :class:`django.utils.feedgenerator.RssUserland091Feed` |
| * :class:`django.utils.feedgenerator.Rss201rev2Feed` |
| * :class:`django.utils.feedgenerator.Atom1Feed` |
| |
| Each of these three classes knows how to render a certain type of feed as XML. |
| They share this interface: |
| |
| :meth:`.SyndicationFeed.__init__` |
| Initialize the feed with the given dictionary of metadata, which applies to |
| the entire feed. Required keyword arguments are: |
| |
| * ``title`` |
| * ``link`` |
| * ``description`` |
| |
| There's also a bunch of other optional keywords: |
| |
| * ``language`` |
| * ``author_email`` |
| * ``author_name`` |
| * ``author_link`` |
| * ``subtitle`` |
| * ``categories`` |
| * ``feed_url`` |
| * ``feed_copyright`` |
| * ``feed_guid`` |
| * ``ttl`` |
| |
| Any extra keyword arguments you pass to ``__init__`` will be stored in |
| ``self.feed`` for use with `custom feed generators`_. |
| |
| All parameters should be Unicode objects, except ``categories``, which |
| should be a sequence of Unicode objects. |
| |
| :meth:`.SyndicationFeed.add_item` |
| Add an item to the feed with the given parameters. |
| |
| Required keyword arguments are: |
| |
| * ``title`` |
| * ``link`` |
| * ``description`` |
| |
| Optional keyword arguments are: |
| |
| * ``author_email`` |
| * ``author_name`` |
| * ``author_link`` |
| * ``pubdate`` |
| * ``comments`` |
| * ``unique_id`` |
| * ``enclosure`` |
| * ``categories`` |
| * ``item_copyright`` |
| * ``ttl`` |
| |
| Extra keyword arguments will be stored for `custom feed generators`_. |
| |
| All parameters, if given, should be Unicode objects, except: |
| |
| * ``pubdate`` should be a Python :class:`~datetime.datetime` object. |
| * ``enclosure`` should be an instance of |
| :class:`django.utils.feedgenerator.Enclosure`. |
| * ``categories`` should be a sequence of Unicode objects. |
| |
| :meth:`.SyndicationFeed.write` |
| Outputs the feed in the given encoding to outfile, which is a file-like object. |
| |
| :meth:`.SyndicationFeed.writeString` |
| Returns the feed as a string in the given encoding. |
| |
| For example, to create an Atom 1.0 feed and print it to standard output:: |
| |
| >>> from django.utils import feedgenerator |
| >>> from datetime import datetime |
| >>> f = feedgenerator.Atom1Feed( |
| ... title=u"My Weblog", |
| ... link=u"http://www.example.com/", |
| ... description=u"In which I write about what I ate today.", |
| ... language=u"en", |
| ... author_name=u"Myself", |
| ... feed_url=u"http://example.com/atom.xml") |
| >>> f.add_item(title=u"Hot dog today", |
| ... link=u"http://www.example.com/entries/1/", |
| ... pubdate=datetime.now(), |
| ... description=u"<p>Today I had a Vienna Beef hot dog. It was pink, plump and perfect.</p>") |
| >>> print(f.writeString('UTF-8')) |
| <?xml version="1.0" encoding="UTF-8"?> |
| <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"> |
| ... |
| </feed> |
| |
| .. _django/utils/feedgenerator.py: https://github.com/django/django/blob/master/django/utils/feedgenerator.py |
| |
| .. currentmodule:: django.contrib.syndication |
| |
| Custom feed generators |
| ---------------------- |
| |
| If you need to produce a custom feed format, you've got a couple of options. |
| |
| If the feed format is totally custom, you'll want to subclass |
| ``SyndicationFeed`` and completely replace the ``write()`` and |
| ``writeString()`` methods. |
| |
| However, if the feed format is a spin-off of RSS or Atom (i.e. GeoRSS_, Apple's |
| `iTunes podcast format`_, etc.), you've got a better choice. These types of |
| feeds typically add extra elements and/or attributes to the underlying format, |
| and there are a set of methods that ``SyndicationFeed`` calls to get these extra |
| attributes. Thus, you can subclass the appropriate feed generator class |
| (``Atom1Feed`` or ``Rss201rev2Feed``) and extend these callbacks. They are: |
| |
| .. _georss: http://georss.org/ |
| .. _itunes podcast format: http://www.apple.com/itunes/podcasts/specs.html |
| |
| ``SyndicationFeed.root_attributes(self, )`` |
| Return a ``dict`` of attributes to add to the root feed element |
| (``feed``/``channel``). |
| |
| ``SyndicationFeed.add_root_elements(self, handler)`` |
| Callback to add elements inside the root feed element |
| (``feed``/``channel``). ``handler`` is an |
| :class:`~xml.sax.saxutils.XMLGenerator` from Python's built-in SAX library; |
| you'll call methods on it to add to the XML document in process. |
| |
| ``SyndicationFeed.item_attributes(self, item)`` |
| Return a ``dict`` of attributes to add to each item (``item``/``entry``) |
| element. The argument, ``item``, is a dictionary of all the data passed to |
| ``SyndicationFeed.add_item()``. |
| |
| ``SyndicationFeed.add_item_elements(self, handler, item)`` |
| Callback to add elements to each item (``item``/``entry``) element. |
| ``handler`` and ``item`` are as above. |
| |
| .. warning:: |
| |
| If you override any of these methods, be sure to call the superclass methods |
| since they add the required elements for each feed format. |
| |
| For example, you might start implementing an iTunes RSS feed generator like so:: |
| |
| class iTunesFeed(Rss201rev2Feed): |
| def root_attributes(self): |
| attrs = super(iTunesFeed, self).root_attributes() |
| attrs['xmlns:itunes'] = 'http://www.itunes.com/dtds/podcast-1.0.dtd' |
| return attrs |
| |
| def add_root_elements(self, handler): |
| super(iTunesFeed, self).add_root_elements(handler) |
| handler.addQuickElement('itunes:explicit', 'clean') |
| |
| Obviously there's a lot more work to be done for a complete custom feed class, |
| but the above example should demonstrate the basic idea. |