From 65b98992699a80390999c11f117068a97222bf2a Mon Sep 17 00:00:00 2001 From: Jeffrey Hearn Date: Thu, 15 May 2014 22:41:13 -0400 Subject: [PATCH] Routes --- docs/building_your_site.rst | 106 ++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 5 deletions(-) diff --git a/docs/building_your_site.rst b/docs/building_your_site.rst index 1b4c9dd098..04c2e2ae5e 100644 --- a/docs/building_your_site.rst +++ b/docs/building_your_site.rst @@ -122,7 +122,7 @@ Your ``Page``-derived models might have other interrelationships which extend th Model Recipes ~~~~~~~~~~~~~ -Overriding the Serve() Method +Overriding the serve() Method ----------------------------- Wagtail defaults to serving ``Page``-derived models by passing ``self`` to a Django HTML template matching the model's name, but suppose you wanted to serve something other than HTML? You can override the ``serve()`` method provided by the ``Page`` class and handle the Django request and response more directly. @@ -155,6 +155,90 @@ Consider this example from the Wagtail demo site's ``models.py``, which serves a With this strategy, you could use Django or Python utilities to render your model in JSON or XML or any other format you'd like. + +Adding Endpoints with Custom route() Methods +-------------------------------------------- + +Wagtail routes requests by iterating over the path components (separated with a forward slash ``/``), finding matching objects based on their slug, and delegating further routing to that object's model class. The Wagtail source is very instructive in figuring out what's happening. This is the default ``route()`` method of the ``Page`` class: + +.. code-block:: python + + class Page(...): + + ... + + def route(self, request, path_components): + if path_components: + # request is for a child of this page + child_slug = path_components[0] + remaining_components = path_components[1:] + + # find a matching child or 404 + try: + subpage = self.get_children().get(slug=child_slug) + except Page.DoesNotExist: + raise Http404 + + # delegate further routing + return subpage.specific.route(request, remaining_components) + + else: + # request is for this very page + if self.live: + # use the serve() method to render the request if the page is published + return self.serve(request) + else: + # the page matches the request, but isn't published, so 404 + raise Http404 + +The contract is pretty simple. ``route()`` takes the current object (``self``), the ``request`` object, and a list of the remaining ``path_components`` from the request URL. It either continues delegating routing by calling ``route()`` again on one of its children in the Wagtail tree, or ends the routing process by serving something -- either normally through the ``self.serve()`` method or by raising a 404 error. + +By overriding the ``route()`` method, we could create custom endpoints for each object in the Wagtail tree. One use case might be using an alternate template when encountering the ``print/`` endpoint in the path. Another might be a REST API which interacts with the current object. Just to see what's involved, lets make a simple model which prints out all of its child path components. + +First, ``models.py``: + +.. code-block:: python + + from django.shortcuts import render + + ... + + class Echoer(Page): + + def route(self, request, path_components): + if path_components: + return render(request, self.template, { + 'self': self, + 'echo': ' '.join(path_components), + }) + else: + if self.live: + return self.serve(request) + else: + raise Http404 + + Echoer.content_panels = [ + FieldPanel('title', classname="full title"), + ] + + Echoer.promote_panels = [ + MultiFieldPanel(COMMON_PANELS, "Common page configuration"), + ] + +This model, ``Echoer``, doesn't define any properties, but does subclass ``Page`` so objects will be able to have a custom title and slug. The template just has to display our ``{{ echo }}`` property. We're skipping the ``serve()`` method entirely, but you could include your render code there to stay consistent with Wagtail's conventions. + +Now, once creating a new ``Echoer`` page in the Wagtail admin titled "Echo Base," requests such as:: + + http://127.0.0.1:8000/echo-base/tauntaun/kennel/bed/and/breakfast/ + +Will return:: + + tauntaun kennel bed and breakfast + +Lovely, huh? (We know.) + + + Tagging ------- @@ -216,10 +300,10 @@ This is just one possible way of creating a taxonomy for Wagtail objects. With a - custom route methods - ParentalKey for storing groups of stuff to a Page-thing +Extended Page Data with ParentalKey +----------------------------------- @@ -229,11 +313,23 @@ Templates Location -------- - Wagtail looks for templates matching your models in... + +For each of your ``Page``-derived models, Wagtail will look for a template in the following location, relative to your project root:: + + project/ + app/ + templates/ + app/ + blog_index_page.html + models.py + +Class names are converted from camel case to underscores. For example, the template for model class ``BlogIndexPage`` would be assumed to be ``blog_index_page.html``. Self ---- - Without a custom rendering function, a ... + +By default, the context passed to a model's template consists of two properties: ``self`` and ``request``. ``self`` is the model object being displayed. ``request`` is the normal Django request object. + Template Tags -------------