Looking to build a modern, scalable blog with Python? Wagtail CMS offers a sleek, developer‑friendly platform that blends the power of Django with a flexible, drag‑and‑drop editor. In this guide we’ll walk through every step of creating a full‑featured blog project using Wagtail, from setting up the environment to deploying a production‑ready site. Whether you’re a seasoned Django developer or just starting out, this tutorial will give you the tools and best practices you need to launch a high‑performance Python Wagtail blog that ranks well in search engines.
What Is Wagtail CMS?
Wagtail is an open‑source content management system built on top of the Django framework. It was created by the editorial teams at Mozilla and NASA, and has since become a favorite for newsrooms, universities, and businesses that need a clean editorial experience without sacrificing developer control. Key features include:
- Intuitive page tree and rich‑text editor.
- Powerful
StreamFieldfor flexible content blocks. - Built‑in image and document handling with automatic rendition generation.
- Robust permissions system and multi‑language support.
- Full Django compatibility, allowing you to reuse models, forms, and middleware.
Why Choose Wagtail for a Python Blog?
When it comes to building a blog, Wagtail offers several advantages over traditional Django apps or other CMS platforms. Here’s why it stands out:
- Developer‑first architecture: You write Python code for models and views, keeping the project maintainable and testable.
- SEO‑ready out of the box: Automatic sitemap generation, clean URLs, and easy meta tag customization.
- Scalable content modeling: Use
Pagesubclasses andStreamFieldto create reusable blog post layouts. - Rich editorial experience: Content editors can reorder blocks, add images, and preview drafts without touching code.
- Active community & extensions: Plugins for tagging, commenting, and analytics are readily available.
Setting Up the Development Environment
Prerequisites
- Python 3.9 or newer
- Git
- Virtual environment tool (
venvorvirtualenv) - PostgreSQL (recommended for production)
Creating a Virtual Environment
python -m venv wagtail-blog-env
source wagtail-blog-env/bin/activate # On Windows use `wagtail-blog-env\Scripts\activate`
pip install --upgrade pip
Installing Wagtail
pip install wagtail
# Verify installation
wagtail --version
Creating a New Wagtail Project
Wagtail ships with a handy command‑line utility to bootstrap a project. Run the following command and replace myblog with your desired project name:
wagtail start myblog
cd myblog
This creates a Django project pre‑configured with Wagtail’s core apps, a default home app, and a basic settings file.
Initial Database Migration
python manage.py migrate
python manage.py createsuperuser # Follow prompts to set admin credentials
python manage.py runserver
Visit http://127.0.0.1:8000/admin/ and log in with the superuser you just created. You’ll see the Wagtail admin dashboard ready for content creation.
Designing the Blog App
While the starter project includes a home app, we’ll create a dedicated blog app to keep blog‑specific models and templates organized.
Generating the Blog App
python manage.py startapp blog
Add 'blog' to INSTALLED_APPS in myblog/settings/base.py (or myblog/settings.py if you’re using a single settings file).
Defining Blog Models
Wagtail pages are defined by subclassing wagtail.models.Page. Below is a minimal BlogPage model that includes a title, publication date, author, and a StreamField for flexible content.
from django.db import models
from django.utils import timezone
from wagtail.models import Page
from wagtail.fields import StreamField
from wagtail import blocks
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel, MultiFieldPanel
from wagtail.images.blocks import ImageChooserBlock
class BlogPage(Page):
publish_date = models.DateField("Post date", default=timezone.now)
author = models.CharField(max_length=255, default="Admin")
body = StreamField([
('paragraph', blocks.RichTextBlock()),
('image', ImageChooserBlock()),
('quote', blocks.BlockQuoteBlock()),
], use_json_field=True)
content_panels = Page.content_panels + [
MultiFieldPanel([
FieldPanel('publish_date'),
FieldPanel('author'),
], heading="Post Details"),
StreamFieldPanel('body'),
]
class Meta:
ordering = ['-publish_date']
After defining the model, run migrations:
python manage.py makemigrations blog
python manage.py migrate
Registering the Blog Page in the Admin
Wagtail automatically discovers Page subclasses, but you may want to customize the menu. Create blog/wagtail_hooks.py:
from wagtail import hooks
from wagtail.admin.menu import MenuItem
from django.urls import reverse
@hooks.register('register_admin_menu_item')
def register_blog_menu_item():
return MenuItem('Blog Posts', reverse('wagtailadmin_pages:add', args=('blog', 'blogpage')), classnames='icon icon-doc-full')
Adding Blog Features
Categories and Tags
Wagtail’s ClusterTaggableManager makes tagging simple. Install django-taggit and add a TaggableManager to BlogPage:
pip install django-taggit
from taggit.models import TaggedItemBase
from modelcluster.fields import ParentalKey
from modelcluster.tags import ClusterTaggableManager
class BlogPageTag(TaggedItemBase):
content_object = ParentalKey('blog.BlogPage', related_name='tagged_items', on_delete=models.CASCADE)
class BlogPage(Page):
# ... existing fields ...
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
content_panels = Page.content_panels + [
# ... existing panels ...
FieldPanel('tags'),
]
Author Profiles
Instead of a simple text field, you can create a reusable AuthorPage model that editors can link to from each post.
class AuthorPage(Page):
bio = models.TextField(blank=True)
profile_picture = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
content_panels = Page.content_panels + [
FieldPanel('bio'),
ImageChooserPanel('profile_picture'),
]
Then replace the author CharField on BlogPage with a ForeignKey to AuthorPage for richer author data.
Comments Integration
Wagtail doesn’t ship with a comment system, but you can embed third‑party services like Disqus or use Django‑contrib comments. For a lightweight approach, add a Comment model linked to BlogPage and render a simple form in the template.
Customizing Templates and Styling
Wagtail looks for templates in the templates directory of each app. Create blog/templates/blog/blog_page.html and extend your base layout
Leave a Reply