Django RSS Feed Tutorial

Updated

Table of Contents

Django comes with a built-in syndication feed for dynamically generating RSS (Really Simple Syndication) or Atom feeds.

These feeds contain recently updated content on a website, and users can subscribe to them using a feed aggregator like Feedly. For example, this website's RSS feed is located at https://learndjango.com/feed/rss.

Adding a Django syndication feed takes only a few minutes. All we need to do is create a dedicated file—often called feeds.py—that extends the built-in Feed class and then link it to a URL path. That's it!

Models, Views, URLs

For demonstration purposes, here are the models, views, and URLs for a tutorials app similar to what you see on this website. We'll use this as the basis for adding a feed in the next section.

The models file would look as follows:

# tutorials/models.py
from django.contrib.auth import get_user_model
from django.db import models
from django.urls import reverse

class Tutorial(models.Model):
    author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    description = models.CharField(max_length=200)
    slug = models.SlugField(null=False, unique=True)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    is_live = models.BooleanField(default=False)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('tutorial_detail', kwargs={'slug': self.slug})

    class Meta:
        ordering = ['-updated_at']

The views file would support a list page of all tutorials and a detail page for each.

# tutorials/views.py
from django.views.generic import ListView, DetailView

from .models import Tutorial

class TutorialListView(ListView):
    model = Tutorial
    context_object_name = 'tutorial_list'
    template_name = 'tutorials/tutorial_list.html'


class TutorialDetailView(DetailView): 
    model = Tutorial
    context_object_name = 'tutorial'
    template_name = 'tutorials/tutorial_detail.html'

And the existing URLs path would look something like this:

# tutorials/urls.py
from django.urls import path

from .feeds import TutorialFeed
from .views import TutorialListView, TutorialDetailView

urlpatterns = [
    path('<slug:slug>', TutorialDetailView.as_view(), name='tutorial_detail'),
    path('', TutorialListView.as_view(), name='tutorial_list'),
]

Most websites have a similar pattern with a model, a view containing list and detail views, and two URL paths.

Feeds.py

To add RSS or Atom feeds to this app, we need to create a Feed class somewhere and add it to our URL. Doing this within a specific app might make sense if you want multiple feeds. However, if you intend to have one main feed for a project, adding it to the project-level directory is a good idea. That's what we'll do here.

The larger project for this Tutorials app is called django_project. Therefore, we will create a new file called django_project/feeds.py in the project-level directory. Here are the contents of the file:

# django_project/feeds.py
from django.contrib.syndication.views import Feed
from django.template.defaultfilters import truncatewords

from tutorials.models import Tutorial


class RssTutorialsFeeds(Feed):
    title = "Tutorials"
    link = "/latesttutorials/"
    description = "Recent free tutorials on LearnDjango.com."

    def items(self):
        return Tutorial.objects.order_by("-updated_at")[:100]

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

    def item_description(self, item):
        return truncatewords(item.content, 30)

    def item_lastupdated(self, item):
        return item.updated_at

This Feeds file starts by importing the Feed class and the template filter truncatewords at the top. Next, we also import the Tutorial model from the tutorials app.

We define our custom feed by subclassing the Feed class and providing a name, RssTutorialsFeeds here. The title, link, and description are required RSS elements.

The meat of the feed is the following three methods:

  • items retrieves the objects for the feed; in this case the last 100 sorted by updated_at.
  • item_title returns the title
  • item_description returns the description truncated to 30 words
  • item_lastupdated returns the updated_at timestamp

URLS.py

The second step is to create a URL path for the feed in the project-level urls.py file. We need to import our custom feed class, RssTutorialsFeeds, and provide it a new URL pattern.

# django_project/urls.py
from django.contrib import admin
from django.urls import path, include

from .feeds import RssTutorialsFeeds  # new

urlpatterns = [
    path('admin/', admin.site.urls),
    path('tutorials/', include('tutorials.urls')),
    path('feed/rss', RssTutorialsFeeds(), name="tutorial_feed"),  # new
]  

Navigate to http://127.0.0.1:8000/feed/rss in your web browser, and the RSS feed will list all included tutorials (make sure you have added some to your admin for this example to work!).

In Chrome, you will see the XML code for the RSS feed directly, and in Safari, it will ask you to install an RSS feed reader. The free Fluent app is a good choice and supports Linux, macOS, and Windows.

Navbar

The last step is notifying your users about the RSS feed somehow, usually in the header or footer of your website. You can use the named URL, tutorial_feed, to do this. Something like the below:

<a href="{% url 'tutorial_feed' %}">RSS Feed</a>

Atom

Django allows you to publish either an RSS feed, an Atom feed, or both. To add Atom, we can subclass RssTutorialsFeeds to something different.

# django_project/feeds.py
from django.contrib.syndication.views import Feed
from django.template.defaultfilters import truncatewords
from django.utils.feedgenerator import Atom1Feed  # new

from tutorials.models import Tutorial


class RssTutorialsFeeds(Feed):
    title = "Tutorials"
    link = "/latesttutorials/"
    description = "Recent free tutorials on LearnDjango.com."

    def items(self):
        return Tutorial.objects.order_by("-updated_at")[:100]

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

    def item_description(self, item):
        return truncatewords(item.content, 30)

    def item_lastupdated(self, item):
        return item.updated_at


class AtomSiteNewsFeed(RssTutorialsFeeds):  # new
    feed_type = Atom1Feed
    subtitle = RssTutorialsFeeds.description

Then we update the URLconf.

# django_project/urls.py
from django.contrib import admin
from django.urls import path, include

from .feeds import RssTutorialsFeeds  # new

urlpatterns = [
    path('admin/', admin.site.urls),
    path('tutorials/', include('tutorials.urls')),
    path('feed/atom', RssTutorialsFeeds(), name="tutorial_feed"),  # new
]

In your web browser, navigate to http://127.0.0.1:8000/feed/atom to see the Atom feed.

Next Steps

As a batteries-included web framework, Django comes with many built-in features to help. For example, you can add a dynamically-generated sitemap. And if you want to add a favicon to your project, here is a Django Favicon tutorial to help.