Let's take a look at how we can filter blog posts by their URL. Previously we made a Blog Listing Page with the URL of /blog/. In this tutorial we'll add /blog/category/{category_name}/ and filter pages based on the slug in the URL.

Code & steps

This is a continuation post from the original Routable Page Mixin tutorial. This time we'll add URL parameters and filter pages based on a custom route.

Below is the code you can use. Please note, it's purposely not complete. I didn't handle missing categories in the try/except block — that's for you to use as needed and replace with what your application needs.

class BlogListingPage(RoutablePageMixin, Page):

    # ... fields and panels here

    @route(r"^year/(\d+)/(\d+)/$", name="blogs_by_year")
    def blogs_by_year(self, request, year=None, month=None):
        context = self.get_context(request)
        # Implement your BlogDetailPage filter. Maybe something like this:
        # if year is not None and month is not None:
        #     posts =, month=month)
        # else:
        #     # No year and no month were set, assume this is july-2019 only posts
        #     posts =, month=07)
        # print(year)
        # print(month)
        # context["posts"] = posts

        # Note: The below template (latest_posts.html) will need to be adjusted
        return render(request, "blog/latest_posts.html", context)

    @route(r"^category/(?P<cat_slug>[-\w]*)/$", name="category_view")
    def category_view(self, request, cat_slug):
        """Find blog posts based on a category."""
        context = self.get_context(request)

            # Look for the blog category by its slug.
            category = BlogCategory.objects.get(slug=cat_slug)
        except Exception:
            # Blog category doesnt exist (ie /blog/category/missing-category/)
            # Redirect to self.url, return a 404.. that's up to you!
            category = None

        if category is None:
            # This is an additional check.
            # If the category is None, do something. Maybe default to a particular category.
            # Or redirect the user to /blog/ ¯\_(ツ)_/¯

        context["posts"] =[category])

        # Note: The below template (latest_posts.html) will need to be adjusted
        return render(request, "blog/latest_posts.html", context)
Sign up for our newsletter

Get notified about new lessons :)