<a id="routable-page-mixin"></a>

# `RoutablePageMixin`

The `RoutablePageMixin` mixin provides a convenient way for a page to respond on multiple sub-URLs with different views. For example, a blog section on a site might provide several different types of index page at URLs like `/blog/2013/06/`, `/blog/authors/bob/`, `/blog/tagged/python/`, all served by the same page instance.

A `Page` using `RoutablePageMixin` exists within the page tree like any other page, but URL paths underneath it are checked against a list of patterns. If none of the patterns match, control is passed to subpages as usual (or failing that, a 404 error is thrown).

By default a route for `r'^$'` exists, which serves the content exactly like a normal `Page` would. It can be overridden by using `@re_path(r'^$')` or `@path('')` on any other method of the inheriting class.

## Installation

Add `"wagtail.contrib.routable_page"` to your `INSTALLED_APPS`:

```python
INSTALLED_APPS = [
    ...

    "wagtail.contrib.routable_page",
]
```

## The basics

To use `RoutablePageMixin`, you need to make your class inherit from both [`wagtail.contrib.routable_page.models.RoutablePageMixin`](#wagtail.contrib.routable_page.models.RoutablePageMixin) and [`wagtail.models.Page`](../models.md#wagtail.models.Page), then define some view methods and decorate them with `path` or `re_path`.

These view methods behave like ordinary Django view functions, and must return an `HttpResponse` object.
You may use the `RoutablePageMixing.render` method to override the context and template that the default page rendering would use.
If you want to create a more custom response, you may want to use `django.shortcuts.render`.

The `path` and `re_path` decorators from `wagtail.contrib.routable_page.models.path` are similar to [the Django `django.urls` `path` and `re_path` functions](https://docs.djangoproject.com/en/stable/topics/http/urls/). The former allows the use of plain paths and converters while the latter lets you specify your URL patterns as regular expressions.

#### WARNING
Punctuation is currently [not supported](https://github.com/wagtail/wagtail/issues/3653) in `path` or `re_path` patterns.

Here’s an example of an `EventIndexPage` with three views, assuming that an `EventPage` model with an `event_date` field has been defined elsewhere:

```python
import datetime
from django.http import JsonResponse
from wagtail.fields import RichTextField
from wagtail.models import Page
from wagtail.contrib.routable_page.models import RoutablePageMixin, path, re_path


class EventIndexPage(RoutablePageMixin, Page):

    # Routable pages can have fields like any other - here we would
    # render the intro text on a template with {{ page.intro|richtext }}
    intro = RichTextField()

    @path('') # will override the default Page serving mechanism
    def current_events(self, request):
        """
        View function for the current events page
        """
        events = EventPage.objects.live().filter(event_date__gte=datetime.date.today())

        # NOTE: We can use the RoutablePageMixin.render() method to render
        # the page as normal, but with some of the context values overridden
        return self.render(request, context_overrides={
            'title': "Current events",
            'events': events,
        })

    @path('past/')
    def past_events(self, request):
        """
        View function for the past events page
        """
        events = EventPage.objects.live().filter(event_date__lt=datetime.date.today())

        # NOTE: We are overriding the template here, as well as few context values
        return self.render(
            request,
            context_overrides={
                'title': "Past events",
                'events': events,
            },
            template="events/event_index_historical.html",
        )

    # Multiple routes!
    @path('year/<int:year>/')
    @path('year/current/')
    def events_for_year(self, request, year=None):
        """
        View function for the events for year page
        """
        if year is None:
            year = datetime.date.today().year

        events = EventPage.objects.live().filter(event_date__year=year)

        return self.render(request, context_overrides={
            'title': "Events for %d" % year,
            'events': events,
        })

    @re_path(r'^year/(\d+)/count/$')
    def count_for_year(self, request, year=None):
        """
        View function that returns a simple JSON response that
        includes the number of events scheduled for a specific year
        """
        events = EventPage.objects.live().filter(event_date__year=year)

        # NOTE: The usual template/context rendering process is irrelevant
        # here, so we'll just return a HttpResponse directly
        return JsonResponse({'count': events.count()})
```

### Rendering other pages

Another way of returning an `HttpResponse` is to call the `serve` method of another page. (Calling a page’s own `serve` method within a view method is not valid, as the view method is already being called within `serve`, and this would create a circular definition).

For example, `EventIndexPage` could be extended with a `next/` route that displays the page for the next event:

```python
@path('next/')
def next_event(self, request):
    """
    Display the page for the next event
    """
    future_events = EventPage.objects.live().filter(event_date__gt=datetime.date.today())
    next_event = future_events.order_by('event_date').first()

    return next_event.serve(request)
```

### Reversing URLs

[`RoutablePageMixin`](#wagtail.contrib.routable_page.models.RoutablePageMixin) adds a [`reverse_subpage()`](#wagtail.contrib.routable_page.models.RoutablePageMixin.reverse_subpage) method to your page model which you can use for reversing URLs. For example:

```python
    # The URL name defaults to the view method name.
    >>> event_page.reverse_subpage('events_for_year', args=(2015, ))
    'year/2015/'
```

This method only returns the part of the URL within the page. To get the full URL, you must append it to the values of either the [`get_url()`](../models.md#wagtail.models.Page.get_url) method or the [`full_url`](../models.md#wagtail.models.Page.full_url) attribute on your page:

```python
>>> event_page.get_url() + event_page.reverse_subpage('events_for_year', args=(2015, ))
'/events/year/2015/'

>>> event_page.full_url + event_page.reverse_subpage('events_for_year', args=(2015, ))
'https://example.com/events/year/2015/'
```

### Changing route names

The route name defaults to the name of the view. You can override this name with the `name` keyword argument on `@path` or `re_path`:

```python
from wagtail.models import Page
from wagtail.contrib.routable_page.models import RoutablePageMixin, re_path


class EventPage(RoutablePageMixin, Page):
    ...

    @re_path(r'^year/(\d+)/$', name='year')
    def events_for_year(self, request, year):
        """
        View function for the events for year page
        """
        ...
```

```python
>>> event_page.url + event_page.reverse_subpage('year', args=(2015, ))
'/events/year/2015/'
```

## The `RoutablePageMixin` class

### *class* wagtail.contrib.routable_page.models.RoutablePageMixin

This class can be mixed in to a Page model, allowing extra routes to be
added to it.

#### route(request, path_components)

This hooks the subpage URLs into Wagtail’s routing.

This method overrides the default [`Page.route()`](../models.md#wagtail.models.Page.route)
method to route requests to the appropriate view method.

It sets `routable_resolver_match` on the request object to make sub-URL routing
information available downstream in the same way that Django sets
[`request.resolver_match`](https://docs.djangoproject.com/en/stable/ref/request-response/#django.http.HttpRequest.resolver_match).

#### render(request, \*args, template=None, context_overrides=None, \*\*kwargs)

This method replicates what `Page.serve()` usually does when `RoutablePageMixin`
is not used. By default, `Page.get_template()` is called to derive the template
to use for rendering, and `Page.get_context()` is always called to gather the
data to be included in the context.

You can use the `context_overrides` keyword argument as a shortcut to override or
add new values to the context. For example:

```python
@path('') # override the default route
def upcoming_events(self, request):
    return self.render(request, context_overrides={
        'title': "Current events",
        'events': EventPage.objects.live().future(),
    })
```

You can also use the `template` argument to specify an alternative
template to use for rendering. For example:

```python
@path('past/')
def past_events(self, request):
    return self.render(
        request,
        context_overrides={
            'title': "Past events",
            'events': EventPage.objects.live().past(),
        },
        template="events/event_index_historical.html",
    )
```

#### *classmethod* get_subpage_urls()

#### resolve_subpage(path)

This method takes a URL path and finds the view to call.

Example:

```python
view, args, kwargs = page.resolve_subpage('/past/')
response = view(request, *args, **kwargs)
```

#### reverse_subpage(name, args=None, kwargs=None)

This method takes a route name/arguments and returns a URL path.

Example:

```python
url = page.url + page.reverse_subpage('events_for_year', kwargs={'year': '2014'})
```

<a id="routablepageurl-template-tag"></a>

## The `routablepageurl` template tag

### wagtail.contrib.routable_page.templatetags.wagtailroutablepage_tags.routablepageurl(context, page, url_name, \*args, \*\*kwargs)

`routablepageurl` is similar to `pageurl`, but works with
pages using `RoutablePageMixin`. It behaves like a hybrid between the built-in
`reverse`, and `pageurl` from Wagtail.

`page` is the RoutablePage that URLs will be generated from.

`url_name` is a URL name defined in `page.subpage_urls`.

Positional arguments and keyword arguments should be passed as normal
positional arguments and keyword arguments.

Example:

```html+django
{% load wagtailroutablepage_tags %}

{% routablepageurl page "feed" %}
{% routablepageurl page "archive" 2014 08 14 %}
{% routablepageurl page "food" foo="bar" baz="quux" %}
```
