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:
- Object Retrieval: Calls
get_object()to fetch any context object based on URL parameters. - Generator Initialization: Initializes a
feedgenerator(defaulting to RSS 2.0) with feed-level metadata liketitleandlink. - Item Processing: Iterates over the collection returned by
items(), mapping each item to a feed entry using methods likeitem_title()anditem_link(). - Response Generation: Returns an
HttpResponsewith the correct content type and, if publication dates are available, aLast-Modifiedheader 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()),