Skip to main content

Creating Syndication Feeds

The syndication framework in this codebase provides a high-level API for generating RSS and Atom feeds. At its core is the Feed class, which acts as a specialized Django view that maps model data to feed elements.

The Feed Class

The Feed class, located in django.contrib.syndication.views, manages the lifecycle of a feed request. When a Feed instance is called as a view, it performs the following steps:

  1. Object Retrieval: Calls get_object() to fetch any context object based on URL parameters.
  2. Generator Initialization: Initializes a feedgenerator (defaulting to RSS 2.0) with feed-level metadata like title and link.
  3. Item Processing: Iterates over the collection returned by items(), mapping each item to a feed entry using methods like item_title() and item_link().
  4. Response Generation: Returns an HttpResponse with the correct content type and, if publication dates are available, a Last-Modified header for conditional GET support.

Basic Implementation

A standard feed is created by subclassing Feed and defining basic metadata and the items() method.

from django.contrib.syndication.views import Feed
from myapp.models import Entry

class LatestEntriesFeed(Feed):
title = "My Blog Updates"
link = "/blog/"
description = "Latest posts from my blog."

def items(self):
return Entry.objects.order_by("-published")[:5]

def item_title(self, item):
return item.title

def item_description(self, item):
return item.summary

By default, the framework attempts to call get_absolute_url() on each item to determine the item_link. If your model does not implement this, you must provide an item_link() method in your Feed class.

Dynamic Feeds

Feeds often need to be filtered based on parameters in the URL (e.g., a feed for a specific category). This is handled by overriding get_object().

As seen in tests/syndication_tests/feeds.py, get_object receives the request and any arguments from the URL configuration:

class CategoryFeed(Feed):
def get_object(self, request, category_id):
return Category.objects.get(pk=category_id)

def title(self, obj):
return "Entries in category: %s" % obj.name

def items(self, obj):
return Entry.objects.filter(category=obj).order_by("-published")

The object returned by get_object() is passed as the first argument to nearly every other feed method, allowing for dynamic metadata and item filtering.

Customizing Item Content

The framework provides two primary ways to generate the content for feed items: method-based mapping and template-based rendering.

Method-Based Mapping

You can define methods that take the item as an argument. The framework uses _get_dynamic_attr internally to determine if an attribute is a static value or a callable.

def item_pubdate(self, item):
return item.published

def item_categories(self, item):
return [tag.name for tag in item.tags.all()]

Template-Based Rendering

For complex HTML descriptions or titles, you can use Django templates by setting title_template or description_template.

class TemplateFeed(Feed):
title_template = "syndication/title.html"
description_template = "syndication/description.html"

When templates are used, the framework calls get_context_data() to provide variables to the template. By default, this context includes obj (the item), site (the current site), and request.

Feed Types and Formats

The default format is RSS 2.0. To switch to Atom 1.0 or another format, change the feed_type attribute using a class from django.utils.feedgenerator.

from django.utils import feedgenerator

class AtomEntriesFeed(LatestEntriesFeed):
feed_type = feedgenerator.Atom1Feed
subtitle = LatestEntriesFeed.description

Media Enclosures

The framework supports media attachments (enclosures) through item_enclosures(). This is particularly useful for podcasting or image feeds.

def item_enclosures(self, item):
return [
feedgenerator.Enclosure(
url=item.image.url,
length=str(item.image.size),
mime_type="image/jpeg"
)
]

Note that while Atom feeds support multiple enclosures per item, the RSS 2.0 specification limits items to a single enclosure.

Integration and Configuration

The syndication app is configured via SyndicationConfig in django.contrib.syndication.apps. To use the framework, ensure django.contrib.syndication is in your INSTALLED_APPS.

To expose a feed, instantiate it directly in your URLconf:

from django.urls import path
from .feeds import LatestEntriesFeed

urlpatterns = [
path("rss/latest/", LatestEntriesFeed()),
]

If your feed uses get_object, ensure the URL pattern captures the necessary arguments:

path("rss/category/<int:category_id>/", CategoryFeed()),