[PYTHON] Wagtail Recommendation (4) Let's pass the context to the template

Introduction

Even in Wagtail, context information is passed to the template when rendering the page. With the simple usage we've seen so far, you didn't have to be aware of it, but it might be useful to be able to add your own information to the context. Wagtail provides an easy way to do that.

This time, let's add the ListPage class and keep the standard flow for rendering the page after adding information to the context.

Add page class

This time as well, add one definition of a new page class (ListPage class). So, first, let's extend cms/models.py as below.

from django.db import models

from modelcluster.fields import ParentalKey

from wagtail.core.models import Page, Orderable
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, PageChooserPanel
from wagtail.images.edit_handlers import ImageChooserPanel

class TopPage(Page):
    ...

    subpage_types = ['cms.ListPage', 'cms.PlainPage']

class PlainPage(Page):
    ...

class ListPage(Page):
    cover_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    intro = models.CharField(max_length=255)
    main_body = RichTextField(blank=True)

    content_panels = Page.content_panels + [
        ImageChooserPanel('cover_image'),
        FieldPanel('intro'),
        FieldPanel('main_body', classname="full"),
        InlinePanel('related_pages', label="Related Pages"),
    ]

    parent_page_types = ['cms.TopPage', 'cms.ListPage']

    def get_top_page(self):
        pages = self.get_ancestors().type(TopPage)
        return pages[0]

class NavItems(Orderable):
    ...

class RelatedPages(Orderable):
    base_page = ParentalKey(Page, related_name='related_pages')
    page = models.ForeignKey(
        Page,
        on_delete=models.CASCADE,
        related_name='+'
    )
    panels = [
        PageChooserPanel('page'),
    ]

You can see that the definition of the ListPage class and the new Orderable subclass RelatedPages used there has been added. RelatedPages is a tool for listing related pages to be displayed on the page of the ListPage class. It's almost the same as the NavItems we covered last time, so no detailed explanation is necessary.

The definition of the ListPage class is almost the same as PlainPage except that this RelatedPages is associated. According to the specification of parent_page_types, this page can be positioned as a child element of TopPage or ListPage itself.

In response to the addition of the ListPage class definition, the child element specification was also added to the TopPage class.

Add unique information to context

Next, let's get into the main subject of this time. In other words, let's add unique information to the context passed to the template. This can be easily achieved by overriding the get_context () method of the Page class. Let's look at a concrete example using ListPage as a subject.

class ListPage(Page):
    ...

    def get_breads(self):
        breads = self.get_ancestors().descendant_of(self.get_top_page(), True)
        return breads

    def get_context(self, request):
        context = super().get_context(request)
        context['top_page'] = self.get_top_page()
        context['breads'] = self.get_breads()
        context['page_list'] = [item.page for item in self.related_pages.all()]
        return context

Before discussing the get_context () method, let's take a quick look at the get_breads () method. This is a method that retrieves all the pages from the parent-child relationship back to the top page and returns a list arranged from the top page in reverse order. This will be used for the breadcrumb trail that will be implemented later.

Next, look at the get_context () method. After calling the get_context () method of the superclass (ie the default Page class), you can see that it adds a new element to the dictionary context it returns. Specifically, it is the top page obtained by the get_top_page () method, the breadcrumb trail information obtained by the get_breads () method, and the list of RelatedPages associated with this page. By doing this, the information can be easily accessed from the template side.

In fact, this information can be accessed from the template without having to include it in the context, as we saw in the previous implementation of the navigation bar. Therefore, it may be difficult to realize the power of the get_context () method. However, this method can also include information in the context that is difficult to access directly from the template, as evidenced by the fact that it takes a request as an argument (I'll see such an example in the future). ).

In order to unify the templates, let's apply the same extension to the TopPage class and the PlainPage class as shown below.

class TopPage(Page):
    ...

    def get_context(self, request):
        context = super().get_context(request)
        context['top_page'] = self.get_top_page()
        return context

class PlainPage(Page):
    ...

    def get_breads(self):
        breads = self.get_ancestors().descendant_of(self.get_top_page(), True)
        return breads

    def get_context(self, request):
        context = super().get_context(request)
        context['top_page'] = self.get_top_page()
        context['breads'] = self.get_breads()
        return context

List page template

Next, let's make it possible to display the page of the newly added ListPage class. For that purpose, we prepared the following templates, templates/cms/list_page.html.

{% extends 'cms/plain_page.html' %}
{% load wagtailcore_tags %}

{% block main_body %}
    {{ block.super }}

    {% for item_page in page_list %}
        <hr>
        <div class="my-4">
            <a href="{% pageurl item_page %}">
                <h4>{{ item_page.title }}</h4>
                {{ item_page.specific.intro }}
            </a>
            {% if item_page.last_published_at <= item_page. %}
                <p>Posted on {{ item_page.first_published_at|date }}</p>
            {% else %}
                <p>Last modified on {{ item_page.last_published_at|date }}</p>
            {% endif %}
        </div>
    {% endfor %}
    <hr>
{% endblock %}

It can be seen that after inheriting templates/cms/plain_page.html, the rich text is displayed in the main_body block, and then the page information contained in the context page_list is displayed one by one. Note that first_published_at and last_published_at are fields that the default Page class has, and the values ​​of the first publication date and the last update date are automatically entered respectively.

As in this example, the value of the information added to the context can be accessed directly using that key. Therefore, the description of templates/cms/plain_page.html can be simplified a little. Specifically, let's change all the parts that are page.get_top_page to top_page.

Breadcrumb trail implementation

Next, let's implement a breadcrumb trail using the information breads added to the context. Add the display code in templates/cms/plain_page.html so that it will be displayed on all pages. Specifically, a new breadcrumb block was inserted just before the main_body block in the main block as shown below.

...
{% block main %}
    ...
    {% block breadcrumb %}
        <nav class="my-2">
            <ol class="breadcrumb">
                {% for bread in breads %}
                    <li class="breadcrumb-item">
                        <a href="{% pageurl bread %}">
                            {{ bread.title }}
                        </a>
                    </li>
                {% endfor %}
                <li class="breadcrumb-item active">
                    {{ page.title }}
                </li>
            </ol>
        </nav>
    {% endblock %}
    {% block main_body %}
        ....
    {% endblock %}
    ....
{% endblock %}
....

From the context, you can see that the key breads is used to access the list of pages to be displayed in the breadcrumb trail, the pages are extracted one by one, and the link with the title is displayed.

in conclusion

This time, I introduced a simple way to add unique information to the context passed to the template when rendering the page, and used it to implement a breadcrumb trail.

Next time, let's add the last page class PostPage and get used to using the convenient field called StraemField used in it.

Link

-Wagtail Recommendations (1) A story about choosing Wagtail by comparing and examining Django-based CMS -Wagtail Recommendation (2) Let's define, edit, and display the page -Wagtail Recommendation (3) Understand and use the tree structure of the page -Wagtail Recommendation (5) Let's add your own block to StreamField -Wagtail Recommendation (6) Let's add categories and tags

Recommended Posts

Wagtail Recommendation (4) Let's pass the context to the template
In omegaconf, let's pass the direct parameter file to the function
How to pass values to JavaScript variables directly from the [Django] template tag
Introduction to Python Let's prepare the development environment
Let's understand how to pass arguments (Python version)
[Wagtail] Add a login page to the Wagtail project
Let's apply the brand image color to the matplotlib colormap!
Template of python script to read the contents of the file
Do not pass self to ProcessPoolExecutor in the class
Let's add it to the environment variable from the command ~
I touched Wagtail (1) and let's override the save method.