From this time, I will divide it into several times and try to actually build a simple CMS with Wagtail. As a goal, I imagined a site that hosts pages for multiple research projects and research groups.
Let's name the top page of each research project or research group TopPage
. Below that, pages such as project outline explanations and member introductions will be placed, but the pages used for them will be called Plain Page
. In addition, let's prepare a blog post-like page PostPage
and a pageListPage
that displays a list of related blog posts together.
For the time being, leave the HomePage
provided by default as it is, so that multiple TopPage
s can be hung under it. Then, under TopPage
, PlainPage
or ListPage
can be hung, and under ListPage
, another ListPage
or PostPage
can be hung.
This time, let's take a look at the general flow from actually defining a page, editing it, and displaying it, using TopPage
as the subject.
The HomePage
, TopPage
, PlainPage
, ListPage
, PostPage
, etc. introduced above represent the types of pages managed by the CMS. Wagtail defines these pages as subclasses that inherit from the Page
class.
The definition of HomePage
prepared by default is described in home/models.py as follows.
from wagtail.core.models import Page
class HomePage(Page):
pass
That is, HomePage
has nothing added to the Page
class by default.
When you log in to the Wagtail management site as a superuser, you should see only one page with the title "Welcome to your new Wagtail site!" Registered. This corresponds to an instance of the HomePage
class. When you open the edit screen of this page, there are three tabs, CONTENT, PROMOTE, and SETTINGS, and you can add some information from each tab. These pieces of information are examples of information that the Page
class can hold.
When defining your own page, by inheriting the Page
class, you will add information (fields and methods) other than those originally provided in the Page
class. There are two things to consider at this time.
――What information should the editor enter from the management site for that page? --What information should be passed to the template to display (render) the page?
The answers to these items do not always match. This is because some of the latter items may be automatically acquired by another method even if the editor does not input them. Here, first, after adding the information corresponding to the former item as a field to the Page
class, let's expand the edit screen so that they can be input from the management site.
The definition of the self-made page is described in the model module in the cms application added last time, that is, in cms/models.py. Here, the TopPage
class is defined as shown below.
from django.db import models
from wagtail.core.models import Page
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.images.edit_handlers import ImageChooserPanel
class TopPage(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)
side_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
side_title = models.CharField(blank=True, max_length=255)
side_body = RichTextField(blank=True)
content_panels = Page.content_panels + [
ImageChooserPanel('cover_image'),
FieldPanel('intro'),
FieldPanel('main_body', classname="full"),
ImageChooserPanel('side_image'),
FieldPanel('side_title'),
FieldPanel('side_body', classname="full"),
]
You can see that intro
(information in the introductory text of the page) and side_title
(sidebar title) have been added as Django's models.CharField
. In addition, main_body
(page body) and side_body
(sidebar body) have been added using Wagtail's unique field RichTextFIeld
. RichTextFIeld
is for passing a document input from the edit screen in rich text format to a template with an appropriate html tag, and can be said to be an indispensable field for CMS.
You can also see how to add image information by looking at cover_image
and side_image
. Specifically, you can use Django's models.ForeignKey
to associate it with the wagtailimages.Image
class.
Finally, you can see that the panel information about the newly added field has been added to the list of content_panels
. This specifies the format of the input form for inputting information about those fields from the CONTENT tab of the page edit screen of the management site. ImageChooserPanel
is specified for images, and FieldPanel
is specified for others (for the time being, if you specify FieldPanel
for other than images, an appropriate input form will be prepared according to the field type. You should think about it).
Next, let's edit the page defined above on the page edit screen of the management site. By doing so, you can also see how the specification added to content_panels
is reflected on the edit screen (for example, check how the edit screen changes if you remove the specification ofclass name = "full"
. You should try it).
Specifically, select "Welcome to your new Wagtail site!" From the page list, click ADD CHILD PAGE instead of EDIT, and select TopPage
to edit the newTopPage
instance. Opens. The editing itself is intuitive, so no explanation is necessary. Let's freely add information and create a sample instance.
The Slug on the PROMOTE tab contains an automatically generated character string, but if you change this, you can change the URL (at the end) when this page is published.
Now you have defined your own TopPage
class and created an instance of it. Next, let's display this on a browser. The URL of the page is determined based on the Slag string above.
In a normal Django application, when you access a given address, the view function associated with it is called in urls.py. Then, the general flow is that the view function generates an appropriate context and incorporates it into the template to render the page. Therefore, in order to be able to display the page, it was necessary to add the routing specification to urls.py and prepare the view function and template.
On the other hand, in Wagtail, routing is done automatically, so there is no need to edit urls.py, and in standard usage, it is not necessary to prepare something like a view function. If only the template is prepared, the page will be rendered automatically based on the instance information.
So, let's prepare a template first. Templates used by pages of the TopPage
class defined in cms/models.py are named templates/cms/top_page.html by default (note that the Pascal case in the class name is the snake case in the template). Try). Normally, you can just use the default name.
Since it is difficult to create templates/cms/top_page.html from scratch, we will inherit from templates/base.html provided by Wagtail by default. Templates/base.html contains blocks called extra_css for adding style files, content for adding content, and extra_js for adding javaScript, so use those blocks.
In order to improve the perspective of template inheritance, we first created templates/cms/base.html as follows.
{% extends 'base.html' %}
{% load static %}
{% block extra_css %}
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk"
crossorigin="anonymous"
/>
<link rel="stylesheet" type="text/css" href="{% static 'css/cms/base.css' %}">
{% endblock %}
{% block content %}
<div id="nav_bar">{% block nav_bar %}{% endblock %}</div>
<div id="header">{% block header %}{% endblock %}</div>
<div id="main">{% block main %}{% endblock %}</div>
<div id="footer">{% block footer %}{% endblock %}</div>
{% endblock %}
{% block extra_js %}
<script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
<script
src="https://unpkg.com/react-bootstrap@next/dist/react-bootstrap.min.js"
crossorigin></script>
{% endblock %}
In the extra_css block, css for Bootstarap and self-made css/cms/base.css are read. In the content block, the contents of the <body>
tag are further subdivided into four <div>
with ids nav_bar, header, main, and footer. Also, in the extra_js block, the scripts for React and React Bootstrap are loaded (I use React Bootstrap because I want to use React in other places. If I just use Bootstrap, jQuery is normal. Should be taken in).
The description of my own css was kept to a minimum. The contents of css/cms/base.css are as follows.
body {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
padding: 50px 0 50px 0;
}
.jumbotron {
height: 400px;
color: #ffffff;
background-size: cover;
background-position: center center;
background-color: rgba(0, 0, 0, 0.5);
background-blend-mode: multiply;
}
#main > .container {
max-width: 992px;
}
#footer {
font-size: 90%;
}
/*The following description is a specification to make the embedded media responsive.*/
.rich-text img {
max-width: 100%;
height: auto;
}
.responsive-object {
position: relative;
}
.responsive-object iframe,
.responsive-object object,
.responsive-object embed {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
The second half is a specification for making images and other embedded media responsive (see here for details). In addition, add the following description to config/settings/base.py.
WAGTAILEMBEDS_RESPONSIVE_HTML = True
Then, based on these, templates/cms/top_page.html was created as shown below.
{% extends 'cms/base.html' %}
{% load static wagtailcore_tags wagtailimages_tags %}
{% block nav_bar %}
<nav class="navbar fixed-top navbar-dark bg-dark mb-0">
<span class="navbar-text mx-auto">
Say something at the begining.
</span>
</nav>
{% endblock %}
{% block header %}
{% image page.cover_image fill-1000x400 as my_image %}
<div class="jumbotron jumbotron-fluid" style="background-image: url('{{ my_image.url }}');">
<div class="container">
<h1 class="display-4">{{ page.title }}</h1>
<p class="lead">{{ page.intro }}</p>
</div>
</div>
{% endblock %}
{% block main %}
<div class="container">
<div class="row">
<div class="col-md-8">
{% block main_body %}
<div class="rich-text my-5">
{{ page.main_body|richtext }}
</div>
{% endblock %}
</div>
<div class="col-md-4">
{% block side_bar %}
<div class="card my-4">
<h4 class="card-header">{{ page.side_title}}</h4>
<div class="card-body">
{% image page.side_image fill-200x200 class="img-fluid rounded-circle d-block mx-auto" alt="" %}
</div>
<div class="card-body">
<div class="rich-text">
{{ page.side_body|richtext }}
</div>
</div>
</div>
{% endblock %}
</div>
</div>
</div>
{% endblock %}
{% block footer %}
<nav class="navbar navbar-dark bg-dark fixed-bottom">
<span class="navbar-text mx-auto py-0">
Say something at the end.
</span>
</nav>
{% endblock %}
{% block extra_js %}
{{ block.super }}
{% endblock %}
By loading wagtailcore_tags and wagtailimages_tags, you can use the template tags provided by Wagtail (image tag and richtext filter in the above example). Also, it can be seen that the field information of the page instance can be accessed with page.field name
. Since the html tag is escaped in the document stored in the RichText field, it is restored through the richtext filter. Also, by enclosing that part in <div class =" rich-text ">
, the following specifications described in css/cms/base.css are effective (for details, here). See).
.rich-text img {
max-width: 100%;
height: auto;
}
Here, we will not go into how to handle images using the image tag (but see here for details). The nav_bar block and footer block are dummies. Jumbotron with the image specified by cover_image as the background is displayed in the header block. Bootstrap card is used for the sidebar.
This time, I briefly introduced the flow of page definition, editing, and display in Wagtail. I think it's a lot easier than creating a similar page in plain Django. Personally, I also like the ease of use of the management site. Adjusting the appearance can be a bit tedious, but you can also use the publicly available Bootstrap template (see This Tutorial for details).
From the next time onward, I would like to touch on detailed topics little by little.
-Wagtail Recommendation (1) A story about choosing Wagtail by comparing and examining Django-based CMS -Wagtail Recommendation (3) Understand and use the tree structure of the page -Wagtail Recommendation (4) Let's pass context to template -Wagtail Recommendation (5) Let's add your own block to StreamField -Wagtail Recommendation (6) Add Categories and Tags
Recommended Posts