Paginate Django Feeds

sounak98
Published: 08/26/2019

Django has a great framework for generating feeds, but sadly it doesn't support pagination out of the box. Currently we have tons of blogs and the feeds page was loading especially slow with all the pages in it. I was hoping django will have some class variable to enable pagination but I was out of luck. But it had a method which took in the request, so I knew I had to parse the page number from there.

from django.contrib.syndication.views import Feed

class BlogsFeed(Feed):
    ...
    def get_objects(self, request):
        ...
        page = request.GET.get("p", 1)
        ...
        return queryset_from_page

So in the get_objects method we need to get the page number from the GET args and then return the queryset according to the page number.

That seems to be enought right? Even I thought so. But the RSS standards say that we also need to add entries like url for last page number, first page number and the current page number. Well, we can even do that. The class BlogsFeed takes in a class variable called feed_type and we need to set it to the feed type class and django provides a class for that too called DefaultFeed. Well we will inherit this DefaultFeed and make out own type of Feed which will include the page numbers too.

from django.utils.feedgenerator import DefaultFeed

class PaginateFeed(DefaultFeed):
    content_type = "application/xml; charset=utf-8"

    def add_root_elements(self, handler):
        super(CorrectMimeTypeFeed, self).add_root_elements(handler)
        if self.feed["page"] is not None:
            if not self.feed["show_all_articles"]:
                if (
                    self.feed["page"] >= 1
                    and self.feed["page"] <= self.feed["last_page"]
                ):
                    handler.addQuickElement(
                        "link",
                        "",
                        {
                            "rel": "first",
                            "href": f"{self.feed['feed_url']}?y={self.feed['year']}&p=1",
                        },
                    )
                    handler.addQuickElement(
                        "link",
                        "",
                        {
                            "rel": "last",
                            "href": (
                                f"{self.feed['feed_url']}?y={self.feed['year']}"
                                f"&p={self.feed['last_page']}"
                            ),
                        },
                    )
                    if self.feed["page"] > 1:
                        handler.addQuickElement(
                            "link",
                            "",
                            {
                                "rel": "previous",
                                "href": (
                                    f"{self.feed['feed_url']}?y={self.feed['year']}"
                                    f"&p={self.feed['page'] - 1}"
                                ),
                            },
                        )
                    if self.feed["page"] < self.feed["last_page"]:
                        handler.addQuickElement(
                            "link",
                            "",
                            {
                                "rel": "next",
                                "href": (
                                    f"{self.feed['feed_url']}?y={self.feed['year']}"
                                    f"&p={self.feed['page'] + 1}"
                                ),
                            },
                        )

This code pretty much explains itself. But there is one catch here too self.feed dict does not have a 'page' or a 'year' key. We need to pass that from our BlogsFeed class. Let's see how.

class BlogsFeed(Feed):
    ...
    feed_type = CorrectMimeTypeFeed
    ...
    def feed_extra_kwargs(self, obj):
        return {
            "page": self.page,
            "last_page": self.last_page,
            "show_all_articles": self.show_all_articles,
            "year": self.year,
        }

That's it guys. Now you have your own paginated feed as per the RSS standards.

DJDT

Versions

Time

Settings from gsoc.settings

Headers

Request

SQL queries from 1 connection

Static files (2312 found, 3 used)

Templates (11 rendered)

Cache calls from 1 backend

Signals

Log messages