[Django]-Custom URL for Wagtail model

6๐Ÿ‘

The RoutablePageMixin is the current (v2.0+) way to accomplish this.

Add the module to your installed apps:

INSTALLED_APPS = [
   ...

   "wagtail.contrib.routable_page",
]

Inherit from both wagtail.contrib.routable_page.models.RoutablePageMixin and wagtail.core.models.Page, then define some view methods and decorate them with the wagtail.contrib.routable_page.models.route decorator:

from wagtail.core.models import Page
from wagtail.contrib.routable_page.models import RoutablePageMixin, route

class EventPage(RoutablePageMixin, Page):
    ...

    @route(r'^$') # will override the default Page serving mechanism
    def current_events(self, request):
        """
        View function for the current events page
        """
        ...

    @route(r'^past/$')
    def past_events(self, request):
        """
        View function for the past events page
        """
        ...

    # Multiple routes!
    @route(r'^year/(\d+)/$')
    @route(r'^year/current/$')
    def events_for_year(self, request, year=None):
        """
        View function for the events for year page
        """
        ...
๐Ÿ‘คA Lee

2๐Ÿ‘

New in Wagtail v0.5 is a mechanism that directly addresses this sort of thing:

Embedding URL configuration in Pages or RoutablePage in v1.3.1

(the docs even have an Blog example!)

๐Ÿ‘คnealtodd

2๐Ÿ‘

I was looking into migrating my blog to a wagtail version and wanted to support my previous url schema and had to solve this exact problem. Luckily I just found a solution and want to share it, hopefully this will be helpful for someone else in the future.

The solution is a 2 Step process.

  1. change the url of a blog page to contain the date as well.
class Blog(Page):
    created = models.DateTimeField(auto_now_add=True)

    def get_url_parts(self, request=None):
        super_response = super().get_url_parts(request)
        # handle the cases of the original implementation
        if super_response is None:
            return None
        (site_id, root_url, page_path) = super_response
        if page_path == None:
            return super_response

        # In the happy case, add the date fields
        # split prefix and slug to support blogs that are nested
        prefix, slug, _ = page_path.rsplit("/", 2)
        return (
            site_id,
            root_url,
            f"{prefix}/{self.created.year}/{self.created.month}/{self.created.day}/{slug}/",
        )
  1. And now we need to make those posts also route-able.
class BlogIndexPage(RoutablePageMixin, Page):
    ...
    def route(self, request, path_components):
        if len(path_components) >= 4:
            year, month, day, slug, *rest = path_components
            try:
                subpage = self.get_children().get(slug=slug)
                return subpage.specific.route(request, rest)
            except Page.DoesNotExist:
                ...
        return super().route(request, path_components)

This solution ignores the date and just use the slug to locate a blog post as the original solution. This should also work when you donโ€™t use the RoutablePageMixin.

Hope this is still helpful for someone.

๐Ÿ‘คHWM-Rocker

Leave a comment