Among the fields provided by Wagtail, there is a field called StreamField
, which allows you to combine multiple different blocks in any order to compose content. For example, it is an image of combining code blocks and text blocks in Google Colaboratory.
This time, let's add the PostPage
class and use this StreamField
in it.
So, first define a new page class PostPage
with StreamField
in cms/models.py as follows.
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.core.models import Page, Orderable
from wagtail.core.fields import RichTextField, StreamField
from wagtail.core.blocks import RawHTMLBlock, RichTextBlock
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, PageChooserPanel, StreamFieldPanel
from wagtail.images.edit_handlers import ImageChooserPanel
class TopPage(Page):
...
class PlainPage(Page):
...
class ListPage(Page):
...
subpage_types = ['cms.ListPage', 'cms.PostPage']
...
class PostPage(Page):
cover_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
intro = models.CharField(max_length=250)
main_body = StreamField([
('rich_text', RichTextBlock(icon='doc-full', label='Rich Text')),
('html', RawHTMLBlock(icon='code', label='Raw Html')),
('code', CodeBlock(icon='code', label='Pretty Code')),
])
content_panels = Page.content_panels + [
ImageChooserPanel('cover_image'),
FieldPanel('intro'),
StreamFieldPanel('main_body'),
InlinePanel('related_pages', label="Related Pages"),
]
parent_page_types = ['cms.ListPage']
subpage_types = []
def get_top_page(self):
...
def get_breads(self):
...
def get_context(self, request):
...
class NavItems(Orderable):
...
class RelatedPages(Orderable):
...
It can be seen that it is very similar to the TopPage
, PlainPage
, ListPage
, etc. defined so far. As with the template, for example, based on PlainPage
, you can define another page class as a subclass that inherits it, but we will not go into that here.
The main difference from the previous pages is that main_body
is defined in StreamField
instead of RichTextField
. And if you look at content_panels
, you can see that the editing panel of StreamField
should be specified as StreamFieldPanel
. I don't think there is any need for explanation other than those related to SteamField
.
The blocks that can be used when constructing content with StreamField
are listed in the list in the argument. Of them, RichTextBlock
and RawHTMLBlock
are prepared by Wagtail by default, and their arguments icon
and label
are icons and labels displayed on the edit screen of the management site. Is specified. RichTextBlock
is a block for inputting a document in rich text format, and RawHTMLBlock
is a block for directly inputting html code as it is.
On the other hand, CodeBlock
is a uniquely defined block (inheriting RawHTMLBlock
). This will be explained in a little more detail in the next section.
As an example of adding your own block to StreamField
, let's add a block CodeBlock
to display the syntax highlighted program code using Google Code Prettify. In order to include your own block, you need to prepare a class for that block and a template for displaying it.
First, let's define the CodeBlock
class. Create a file called cms/blocks.py and write the following code in it.
from wagtail.core.blocks import RawHTMLBlock
class CodeBlock(RawHTMLBlock):
class Meta:
template = 'cms/blocks/code.html'
As you can see, CodeBlock
is practically the same as the default RawHTMLBlock
, but the template used for rendering has changed, as specified in Meta
. ..
Next, prepare this template. Add a directory called blocks/under templates/cms /, create a file called code.html in it, and write the following code.
<pre class="prettyprint">
<code>{{ value }}</code>
</pre>
In this way, Google Code Prettify should be applied by specifying class =" prettyprint "
in the <pre>
tag. However, it is necessary to read the script for Google Code Prettify from the CDN. This was added to templates/cms/base.html as follows.
...
{% block extra_js %}
...
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
{% endblock %}
At this stage, log in to the admin site and add some PostPage
pages under one of the ListPage
class pages.
Now that the preparations are complete, proceed to display the page of the PostPage
class. Since PostPage
is a page that imagines a blog post, let's display the posting date and time or the update date and time in Jumbotron. Therefore, first, enclose the text part in the header block of templates/cms/plain_page.html in the header_text block as shown below.
...
{% block header %}
...
<div class="container">
{% block header_text %}
<h1 class="display-4">{{ page.title }}</h1>
<p class="lead">{{ page.intro }}</p>
{% endblock %}
</div>
</div>
{% endblock %}
...
Next, inheriting this templates/cms/plain_page.html, templates/cms/post_page.html was created as follows.
{% extends 'cms/plain_page.html' %}
{% load wagtailcore_tags %}
{% block header_text %}
{{ block.super }}
{% if page.last_published_at > page.first_published_at %}
<span>Last modified on {{ page.last_published_at|date }}</span>
{% else %}
<span>Posted on {{ page.first_published_at|date }}</span>
{% endif %}
{% endblock %}
{% block main_body %}
<div class="rich-text my-5">
{% for block in page.main_body %}
{% include_block block %}
{% endfor %}
</div>
{% 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.first_published_at %}
<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 %}
If you look inside the header_text block, you can see that the last updated date is displayed for pages that have been updated several times, and the posting date is displayed for pages that have not been updated.
Also, if you look at the first <div>
in the main_body block, you can see how to handle StreamField
in the template. Specifically, the for
statement can be used to extract blocks in order and then include_block
.
Let's actually display it on the browser and confirm that the syntax highlighting is applied to the program code.
In the above, I tried to do code highlighting by adding a unique block to StreamField
, but as another method, you can incorporate the function of code highlighting into RichTextField
. Here, as a bonus, I will briefly introduce the method.
As explained in here, the rich text editor Draftail used in Wagtail can be added with its own editing functions.
Here, let's add an editing function for code highlighting. Specifically, create a file called cms/wagtail_hooks.py and write the following contents. As a result, a button called Code is added to the toolbar of the rich text editor. Then, when you select a range and press this button, this editing function is applied to the text in that range. That is, the text will be enclosed in the tag <pre class =" prettyprint ">
as specified in tag
.
import wagtail.admin.rich_text.editors.draftail.features as draftail_features
from wagtail.admin.rich_text.converters.html_to_contentstate import BlockElementHandler
from wagtail.core import hooks
@hooks.register('register_rich_text_features')
def register_code_feature(features):
feature_name = 'code'
type_ = 'code'
tag = 'pre class="prettyprint"'
control = {
'type': type_,
'label': 'Code',
'description': 'code',
'element': 'code',
}
features.register_editor_plugin(
'draftail', feature_name, draftail_features.BlockFeature(control)
)
features.register_converter_rule('contentstate', feature_name, {
'from_database_format': {tag: BlockElementHandler(type_)},
'to_database_format': {'block_map': {type_: tag}},
})
features.default_features.append(feature_name)
This time, I introduced the convenience of StreamField
and also touched on how to add a simple editing function to RichTextField
.
Next time, I would like to add the tag and category functions that are also covered in the tutorial Your first Wagtail site on the official website.
-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 (4) Let's pass context to template -Wagtail Recommendation (6) Add Categories and Tags
Recommended Posts