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
:
INSTALLED_APPS = [
...
"wagtail.contrib.routable_page",
]
The basics¶
To use RoutablePageMixin
, you need to make your class inherit from both :class:wagtail.contrib.routable_page.models.RoutablePageMixin
and 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; typically this is done through a call to 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. The former allows the use of plain paths and converters while the latter lets you specify your URL patterns as regular expressions.
Here’s an example of an EventIndexPage
with three views, assuming that an EventPage
model with an event_date
field has been defined elsewhere:
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
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:
@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
adds a reverse_subpage()
method to your page model which you can use for reversing URLs. For example:
# 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 url
or the full_url
attribute on your page:
>>> event_page.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, ))
'http://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
:
from wagtail.models import Page
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
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
"""
...
>>> 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.
- render(request, *args, template=None, context_overrides=None, **kwargs)¶
This method replicates what
Page.serve()
usually does whenRoutablePageMixin
is not used. By default,Page.get_template()
is called to derive the template to use for rendering, andPage.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:@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:@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:
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:
url = page.url + page.reverse_subpage('events_for_year', kwargs={'year': '2014'})
The routablepageurl
template tag¶
- wagtail.contrib.routable_page.templatetags.wagtailroutablepage_tags.routablepageurl(context, page, url_name, *args, **kwargs)¶
routablepageurl
is similar topageurl
, but works with pages usingRoutablePageMixin
. It behaves like a hybrid between the built-inreverse
, andpageurl
from Wagtail.page
is the RoutablePage that URLs will be generated from.url_name
is a URL name defined inpage.subpage_urls
.Positional arguments and keyword arguments should be passed as normal positional arguments and keyword arguments.
Example:
{% load wagtailroutablepage_tags %}
{% routablepageurl page "feed" %}
{% routablepageurl page "archive" 2014 08 14 %}
{% routablepageurl page "food" foo="bar" baz="quux" %}