Laurence Mercer

How to add Atom feeds for each django-plugin-blog tag

As mentioned in an earlier post, this blog is built using django-plugin-blog. By default, django-plugin-blog outputs a single Atom feed (at /blog/feed/) which lists the five most recent posts.

For reference, see django-plugin-blog's views.py and __init__.py files:

# django_plugin_blog/views.py (abridged)
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Atom1Feed

class BlogFeed(Feed):
    ...
# django_plugin_blog/__init__.py (abridged)
def urlpatterns():
    from .views import BlogFeed

    return [
        path(f"{blog}/feed/", BlogFeed(), name="django_plugin_blog_feed"),
    ]

However, I also wanted feeds for each blog post tag ('django', 'python', etc.). Django's syndication framework, which django-plugin-blog already uses, makes it easy to add both more Atom feeds and, if required, RSS feeds.

I implemented the feed code via a new 'blog' app:

python manage.py startapp blog

Therein, the key file is views.py, which features a BlogTagFeed class that:

  • subclasses Django's syndication Feed class
  • declares its type as Atom (Atom1Feed)
  • defines methods which return various feed and item attributes

Whilst some of the code is specific to django-plugin-blog, such as getting Tag data, the overall approach is universal.

# blog/views.py
from django.conf import settings
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Atom1Feed
from django_plugin_blog.models import Tag


class BlogTagFeed(Feed):
    """Atom feed of latest blog entries for each tag ('django', 'python', etc.)."""
    feed_type = Atom1Feed

    def get_object(self, request, slug):
        return Tag.objects.get(slug=slug)

    def title(self, obj):
        title = getattr(settings, "DJANGO_PLUGIN_BLOG_FEED_TITLE", None) or obj.get_host()
        return f"{title}: Posts tagged '{obj.name}'"

    def link(self, obj):
        return f"/blog/tag/{obj.slug}/"

    def items(self, obj):
        return obj.entry_set.filter(is_draft=False).order_by("-created")[:10]

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

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

    def item_link(self, item):
        return f"/blog/{item.created.year}/{item.slug}/"

    def item_author_name(self, item):
        return (
            ", ".join([a.get_full_name() or str(a) for a in item.authors.all()]) or None
        )

    def get_feed(self, obj, request):
        feedgen = super().get_feed(obj, request)
        feedgen.content_type = "application/xml; charset=utf-8"
        return feedgen

Having created the new feeds, I then updated the site's URLconf. For simplicity, I used the same /feed path naming convention as django-plugin-blog.

# blog/urls.py (abridged)
urlpatterns = [
    path('blog/tag/<slug:slug>/feed/', BlogTagFeed()),
]
# core/urls.py (abridged)
urlpatterns = [
    path('', include('blog.urls')),
]

And that's it. Atom feeds are now available for every blog tag (such as 'django').

Code

For the complete blog app code, see https://github.com/input/lm.

Return to blog