Monday, August 26, 2019

PSF GSoC students blogs: Paginate Django Feeds

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.



from Planet Python
via read more

No comments:

Post a Comment

TestDriven.io: Working with Static and Media Files in Django

This article looks at how to work with static and media files in a Django project, locally and in production. from Planet Python via read...