Python Wagtail Cms Blog Project

Written by

in

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 StreamField for 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:

  1. Developer‑first architecture: You write Python code for models and views, keeping the project maintainable and testable.
  2. SEO‑ready out of the box: Automatic sitemap generation, clean URLs, and easy meta tag customization.
  3. Scalable content modeling: Use Page subclasses and StreamField to create reusable blog post layouts.
  4. Rich editorial experience: Content editors can reorder blocks, add images, and preview drafts without touching code.
  5. 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 (venv or virtualenv)
  • 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

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *