Wagtail Tutorial Series:
To learn more about Wagtail CMS, please check Build Blog With Wagtail CMS (4.0.0)
- Create Wagtail Project
- Modern Frontend Techs for Wagtail
- Dockerizing Wagtail App
- Add Blog Models to Wagtail
- How to write Wagtail page template
- Create Stylish Wagtail Pages with Tailwind CSS
- How to use StreamField in Wagtail
- Wagtail Routable Page
- Add Pagination Component to Wagtail
- Customize Wagtail Page URL
- Add Full Text Search to Wagtail
- Add Markdown Support to Wagtail
- Add LaTeX Support & Code Highlight In Wagtail
- How to Build Form Page in Wagtail
- How to Create and Manage Menus in Wagtail
- Wagtail SEO Guide
- Online Demo http://wagtail-blog.accordbox.com/
- Source code: https://github.com/AccordBox/wagtail-tailwind-blog
Wagtail Tips:
- Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
- Wagtail Tip #2: How to Export & Restore Wagtail Site
Write style in Wagtail:
- How to use SCSS/SASS in your Django project (Python Way)
- How to use SCSS/SASS in your Django project (NPM Way)
Other Wagtail Topics:
Objectives
By the end of this chapter, you should be able to:
- Import Bootstrap theme to Wagtail project
- Build custom Django template tag
Bootstrap
Bootstrap: The most popular HTML, CSS, and JavaScript framework for developing responsive, mobile first projects on the web.
Design
BlogPage
PostPage
Base Template
Let's update wagtail_bootstrap_blog/templates/base.html to import Bootstrap theme files.
{% load static wagtailuserbar %}
<!DOCTYPE html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8" />
<title>
{% block title %}
{% if self.seo_title %}{{ self.seo_title }}{% else %}{{ self.title }}{% endif %}
{% endblock %}
{% block title_suffix %}
{% with self.get_site.site_name as site_name %}
{% if site_name %}- {{ site_name }}{% endif %}
{% endwith %}
{% endblock %}
</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{# Global stylesheets #}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/css/bootstrap.min.css" integrity="sha512-oc9+XSs1H243/FRN9Rw62Fn8EtxjEYWHXRvjS43YtueEewbS6ObfXcJNyohjHqVKFPoXXUxwc+q1K7Dee6vv9g==" crossorigin="anonymous" />
{% block extra_css %}
{# Override this in templates to add extra stylesheets #}
{% endblock %}
</head>
<body class="{% block body_class %}{% endblock %}">
{% wagtailuserbar %}
{% include 'blog/components/navbar.html' %}
<div class="container">
<div class="row">
<div class="col-md-8">
{% block content %}{% endblock %}
</div>
{% include 'blog/components/sidebar.html' %}
</div>
</div>
{% include 'blog/components/footer.html' %}
{# Global javascript #}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.slim.min.js" integrity="sha512-/DXTXr6nQodMUiq+IUJYCt2PPOUjrHJ9wFrqpJ3XkgPNOZVfMok7cRw6CSxyCQxXn6ozlESsSh1/sMCTF1rL/g==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.bundle.min.js" integrity="sha512-iceXjjbmB2rwoX93Ka6HAHP+B76IY1z0o3h+N1PeDtRSsyeetU3/0QKJqGyPJcX63zysNehggFwMC/bi7dvMig==" crossorigin="anonymous"></script>
{% block extra_js %}
{# Override this in templates to add extra javascript #}
{% endblock %}
</body>
</html>
Notes:
- Template inheritance allows you to build a
base “skeleton”
template that contains all the common elements of your site and defines blocks that child templates can override - Here we import
jQuery
,bootstrap
andfont-awesome
from cdnjs, which is a free CDN service provided by CloudFlare. - Then we created a
container
div which hascontent
block and sidebar. - With Django
include
template tag, we can loads a template and renders it with the current context. Django doc
NavBar
Let's create wagtail_bootstrap_blog/templates/blog/components/navbar.html, which is loaded by {% include 'blog/components/navbar.html' %}
in the wagtail_bootstrap_blog/templates/base.html
<nav class="mb-2 navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="/">Wagtail Blog Demo</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
</div>
</div>
</nav>
Footer
Create wagtail_bootstrap_blog/templates/blog/components/footer.html which is loaded by {% include 'blog/components/footer.html' %}
in the wagtail_bootstrap_blog/templates/base.html
<footer class="mt-4 py-5 bg-dark">
<div class="container">
<p class="m-0 text-center text-white">
Built by <a href="https://www.accordbox.com/">MichaelYin@Accordbox</a>
</p>
</div>
</footer>
Sidebar
Create wagtail_bootstrap_blog/templates/blog/components/sidebar.html which is loaded by {% include 'blog/components/sidebar.html' %}
in the wagtail_bootstrap_blog/templates/base.html
<div class="col-md-4">
<h3>Sidebar</h3>
</div>
BlogPage
Update wagtail_bootstrap_blog/templates/blog/blog_page.html
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block content %}
{% for post in page.get_children.specific %}
<div class="card mb-4">
{% if post.header_image %}
{% image post.header_image original as header_image %}
<a href="{% pageurl post %}">
<img src="{{ header_image.url }}" class="card-img-top">
</a>
{% endif %}
<div class="card-body">
<h2 class="card-title">
<a href="{% pageurl post %}">{{ post.title }}</a>
</h2>
<p class="card-text">
{{ post.description }}
</p>
<a href="{% pageurl post %}" class="btn btn-primary">Read More →</a>
</div>
<div class="card-footer text-muted">
Posted on {{ post.last_published_at }}
</div>
</div>
{% endfor %}
{% endblock %}
Notes:
- With Django
extends
template tag, we start using Djangotemplate-inheritance
. - The HTML in
{% block content %}
would override{% block content %}
in wagtail_bootstrap_blog/templates/base.html, you can check Django doc: Template inheritance to learn more
PostPage
Update wagtail_bootstrap_blog/templates/blog/post_page.html
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block content %}
{% image page.header_image original as header_image %}
<img src="{{ header_image.url }}" class="img-fluid">
<h1>{{ page.title }}</h1>
<hr>
<div class="tags">
<h3>Tags</h3>
{% for tag in page.tags.all %}
<button type="button">{{ tag }}</button>
{% endfor %}
</div>
<h3>Categories</h3>
<ul>
{% for postpage_category in page.categories.all %}
<li>
{{ postpage_category.blog_category.name }}
</li>
{% endfor %}
</ul>
<p><a href="{{ page.get_parent.url }}">Return to blog</a></p>
{% endblock %}
Category Widget
Next, let's add Category widget to the sidebar.
To keep the template clean, we will create a custom Django template tag to do this.
Create blog/templatetags/blogapp_tags.py
from blog.models import BlogCategory as Category, Tag
from django.template import Library, loader
register = Library()
@register.inclusion_tag('blog/components/categories_list.html',
takes_context=True)
def categories_list(context):
categories = Category.objects.all()
return {
'request': context['request'],
'categories': categories
}
Notes:
- The logic is very simple, we get all
Category
instances from the DB and display it in template. - The
blog/components/categories_list.html
is the template which will be used to render HTML - You can check Custom template tags and filters
Create wagtail_bootstrap_blog/templates/blog/components/categories_list.html
<div class="card my-4">
<h5 class="card-header">Categories</h5>
<div class="card-body">
<div class="row">
<div class="col-lg-12">
<ul class="list-unstyled mb-0">
{% for category in categories %}
<li>
<a href="#">
{{ category.name }}
</a>
</li>
{% empty %}
'No categories yet'
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
Tag Widget
Update blog/templatetags/blogapp_tags.py
@register.inclusion_tag('blog/components/tags_list.html',
takes_context=True)
def tags_list(context):
tags = Tag.objects.all()
return {
'request': context['request'],
'tags': tags
}
@register.inclusion_tag('blog/components/categories_list.html',
takes_context=True)
def categories_list(context):
categories = Category.objects.all()
return {
'request': context['request'],
'categories': categories
}
Create wagtail_bootstrap_blog/templates/blog/components/tags_list.html
<div class="card my-4">
<h5 class="card-header">Tags</h5>
<div class="card-body">
{% for tag in tags %}
<a href="#">
<span class="badge badge-secondary">{{ tag }}</span>
</a>
{% empty %}
No tags yet
{% endfor %}
</div>
</div>
Sidebar
After we build tags_list
and categories_list
, let's update wagtail_bootstrap_blog/templates/blog/components/sidebar.html
{% load blogapp_tags %}
<div class="col-md-4">
{% if blog_page %}
{% categories_list %}
{% tags_list %}
{% endif %}
</div>
Notes:
- At the top, we
load blogapp_tags
so the above template tags would be available in thesidebar.html
(Likeimport
statement in Python) - We call
categories_list
andtags_list
to render the category list and tag list on the sidebar.
As you can see, now the Category widget and Tag widget is working in the sidebar.
PostPage
Let's keep adding template tags to display category and tag info for specific PostPage
Update blog/templatetags/blogapp_tags.py
@register.inclusion_tag("blog/components/post_categories_list.html", takes_context=True)
def post_categories_list(context):
page = context["page"]
post_categories = page.categories.all()
return {
"request": context["request"],
"post_categories": post_categories,
}
@register.inclusion_tag("blog/components/post_tags_list.html", takes_context=True)
def post_tags_list(context):
page = context["page"]
post_tags = page.tags.all()
return {
"request": context["request"],
"post_tags": post_tags,
}
Notes:
- Here we added two template tags, the
post
prefix tell us they are forpost_page
Create wagtail_bootstrap_blog/templates/blog/components/post_categories_list.html
{% if post_categories %}
<i class="fas fa-tag"></i>
{% for postpage_category in post_categories %}
{{ postpage_category.blog_category.name }}
{% endfor %}
{% endif %}
Create wagtail_bootstrap_blog/templates/blog/components/post_tags_list.html
<div>
{% for tag in post_tags %}
<span class="badge badge-secondary">{{ tag }}</span>
{% endfor %}
</div>
Now if you check the post content, you will see the category and tag info is also displayed.
Wagtail Tutorial Series:
To learn more about Wagtail CMS, please check Build Blog With Wagtail CMS (4.0.0)
- Create Wagtail Project
- Modern Frontend Techs for Wagtail
- Dockerizing Wagtail App
- Add Blog Models to Wagtail
- How to write Wagtail page template
- Create Stylish Wagtail Pages with Tailwind CSS
- How to use StreamField in Wagtail
- Wagtail Routable Page
- Add Pagination Component to Wagtail
- Customize Wagtail Page URL
- Add Full Text Search to Wagtail
- Add Markdown Support to Wagtail
- Add LaTeX Support & Code Highlight In Wagtail
- How to Build Form Page in Wagtail
- How to Create and Manage Menus in Wagtail
- Wagtail SEO Guide
- Online Demo http://wagtail-blog.accordbox.com/
- Source code: https://github.com/AccordBox/wagtail-tailwind-blog
Wagtail Tips:
- Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
- Wagtail Tip #2: How to Export & Restore Wagtail Site
Write style in Wagtail:
- How to use SCSS/SASS in your Django project (Python Way)
- How to use SCSS/SASS in your Django project (NPM Way)
Other Wagtail Topics: