Skip to content

nm-examples/wagtail-prototyper

Repository files navigation

Wagtail Prototyper Setup Guide

A rapid template development system for Wagtail that allows you to build and iterate on templates using YAML fixture files before creating backend models.

Overview

This prototyping system enables frontend-first development by:

  • Creating templates with YAML fixtures instead of database content
  • Providing mock objects that mimic Wagtail's Page and StreamField APIs
  • Offering a dashboard to preview all prototype templates
  • Working only in DEBUG mode (production-safe)

Once your template is complete, create the actual Wagtail page model and the template continues to work without changes.

Installation

1. Copy the Prototypes Folder

Copy the entire app/prototypes/ folder from this repository into your Wagtail project:

# If your project has an app directory structure (e.g., myproject/app/)
cp -r app/prototypes /path/to/your/project/app/

# Or if your apps are at the project root (e.g., myproject/home/, myproject/core/)
cp -r app/prototypes /path/to/your/project/

This includes:

  • views.py - Prototype view logic and mock objects
  • fixtures/ - Directory for YAML fixture files (examples included - you'll create your own)
  • templates/prototypes/dashboard.html - Prototype dashboard template
  • Other app files

Note: The fixtures/ folder contains example fixtures for this demo project's pages (home_page.yaml, standard_page.yaml). These are just examples to show you the structure. You'll delete these and create your own fixture files for your project's templates.

2. Install Dependencies

Add PyYAML to your project:

pip install PyYAML
# or if using uv
uv add PyYAML

3. Add to INSTALLED_APPS

Add the prototypes app to your settings.py:

INSTALLED_APPS = [
    # ... your other apps
    'prototypes',  # or 'app.prototypes' if in app/ subdirectory
]

4. Configure URLs

Add prototype URLs to your main urls.py (only enabled in DEBUG mode):

from django.conf import settings
from django.urls import include, path

# ... your other imports and url patterns

if settings.DEBUG:
    from prototypes import views as prototype_views
    # or: from app.prototypes import views as prototype_views

    urlpatterns += [
        path("prototype/", prototype_views.dashboard, name="prototype_dashboard"),
        path("prototype/<path:template_path>/", prototype_views.prototype_view, name="prototype"),
    ]

5. Adjust Paths (If Needed)

If your project structure differs from this repository, you may need to adjust file paths in prototypes/views.py:

In prototype_view() function (around line 114):

# Current (assumes app/prototypes structure):
fixture_path = Path(settings.BASE_DIR) / 'app' / 'prototypes' / 'fixtures' / fixture_filename

# Adjust if prototypes is at project root:
fixture_path = Path(settings.BASE_DIR) / 'prototypes' / 'fixtures' / fixture_filename

In dashboard() function (around line 154-160):

# Current:
fixtures_dir = Path(settings.BASE_DIR) / 'app' / 'prototypes' / 'fixtures'
app_dir = Path(settings.BASE_DIR) / 'app'

# Adjust based on your project structure
# For example, if apps are at root:
fixtures_dir = Path(settings.BASE_DIR) / 'prototypes' / 'fixtures'
app_dir = Path(settings.BASE_DIR)

Also update the apps to scan (around line 161):

# Skip prototypes app and any system directories
if not app_path.is_dir() or app_path.name.startswith('.') or app_path.name == 'prototypes':
    continue

6. Clean Up Example Fixtures (Optional)

The prototypes/fixtures/ folder contains example fixtures from this demo project. You can delete them and create your own:

# Remove example fixtures (optional)
rm -rf prototypes/fixtures/home/
rm -rf prototypes/fixtures/core/

# Or keep them as reference while you create your own

7. Start Using

Run your development server and visit /prototype/ to see the dashboard:

python manage.py runserver
# Visit: http://localhost:8000/prototype/

Usage

Creating a Prototype Template

  1. Create your template in any app's templates directory:
# Example: create a landing page template
mkdir -p home/templates/home
touch home/templates/home/landing_page.html
  1. Write your template using standard Django/Wagtail template syntax:
{% extends "base.html" %}
{% load wagtailcore_tags %}

{% block content %}
    <h1>{{ page.title }}</h1>

    {% for block in page.body %}
        {% if block.block_type == 'richtext' %}
            <div class="richtext-block">
                {{ block.value|safe }}
            </div>
        {% elif block.block_type == 'image' %}
            <figure class="image-block">
                <img src="{{ block.value.image.url }}" alt="{{ block.value.image.alt }}">
                {% if block.value.caption %}
                    <figcaption>{{ block.value.caption }}</figcaption>
                {% endif %}
            </figure>
        {% endif %}
    {% endfor %}
{% endblock %}
  1. Create a YAML fixture with mock data at prototypes/fixtures/{app}/{template}.yaml:
# Create fixture matching template path
mkdir -p prototypes/fixtures/home
touch prototypes/fixtures/home/landing_page.yaml
# prototypes/fixtures/home/landing_page.yaml
page:
  title: "Welcome to Our Site"
  seo_title: "Home - Our Site"
  search_description: "Welcome to our amazing website"

  # StreamField data
  body:
    - type: richtext
      value: |
        <h2>About Us</h2>
        <p>We build amazing things.</p>

    - type: image
      value:
        url: "https://picsum.photos/1200/600"
        alt: "Hero image"
        caption: "Our amazing product"

    - type: richtext
      value: |
        <p>More content here...</p>

  # Custom fields (simulating ForeignKeys, snippets, etc.)
  cta:
    title: "Get Started"
    description: "Join us today"
    button_text: "Sign Up"
    button_link: "/signup/"
  1. View your prototype:
    • Visit http://localhost:8000/prototype/ to see the dashboard
    • Your template will be listed with fixture status
    • Click "View Prototype" to preview it with the fixture data

Example Fixture Structures

Important: The fixtures included in this repository (app/prototypes/fixtures/home/home_page.yaml and app/prototypes/fixtures/core/standard_page.yaml) are examples from this demo project. They won't work with your project's templates. Use them as reference for structure, then create your own fixtures for your own templates.

Reference the example fixtures for structure:

  • app/prototypes/fixtures/home/home_page.yaml - Shows homepage with StreamField and CTA structure
  • app/prototypes/fixtures/core/standard_page.yaml - Shows content page with multiple block types

StreamField blocks:

page:
  body:
    # Rich text content
    - type: richtext
      value: "<p>HTML content</p>"

    # Headings
    - type: heading
      value: "Section Title"

    # Images
    - type: image
      value:
        url: "https://example.com/image.jpg"
        alt: "Image description"
        caption: "Optional caption"

    # Quotes
    - type: quote
      value:
        quote: "The quote text"
        attribution: "Author Name"

    # Embeds (YouTube, Vimeo, etc.)
    - type: embed
      value:
        url: "https://www.youtube.com/embed/VIDEO_ID"
        caption: "Video caption"

Simulating ForeignKeys and Snippets:

page:
  # Simple field
  subtitle: "A subtitle"

  # Object/snippet (e.g., Author snippet)
  author:
    name: "John Doe"
    bio: "Author biography"
    avatar: "https://example.com/avatar.jpg"

  # Lists
  featured_items:
    - title: "Item 1"
      description: "Description"
      image: "https://example.com/item1.jpg"
    - title: "Item 2"
      description: "Description"
      image: "https://example.com/item2.jpg"

Creating the Real Page Model

Once your template is finalized, create the actual Wagtail page model.

Option 1: Use the Claude Code Skill (Automated)

If you're using Claude Code, there's a built-in skill that can automatically generate the page model from your fixture file:

# In Claude Code, run:
/wagtail-model-creator

# Or mention it in your prompt:
"Create a Wagtail page model from app/prototypes/fixtures/home/landing_page.yaml"

The skill will analyze your fixture structure and generate the complete page model with:

  • Correct field types based on your fixture data
  • StreamField blocks matching your block types
  • Proper imports and content panels
  • Ready-to-use model code

Option 2: Create Manually

Write the page model yourself based on your fixture structure:

# home/models.py
from wagtail.models import Page
from wagtail.fields import StreamField
from wagtail import blocks
from wagtail.admin.panels import FieldPanel
from wagtail.images.blocks import ImageChooserBlock

class LandingPage(Page):
    body = StreamField([
        ('richtext', blocks.RichTextBlock()),
        ('heading', blocks.CharBlock()),
        ('image', blocks.StructBlock([
            ('image', ImageChooserBlock()),
            ('caption', blocks.CharBlock(required=False)),
        ])),
        ('quote', blocks.StructBlock([
            ('quote', blocks.TextBlock()),
            ('attribution', blocks.CharBlock()),
        ])),
    ], use_json_field=True)

    content_panels = Page.content_panels + [
        FieldPanel('body'),
    ]

Run Migrations

Regardless of which option you choose:

python manage.py makemigrations
python manage.py migrate

Your template will now work with real database content without any changes!

How It Works

The prototype system includes several mock classes in prototypes/views.py:

  • MockPage - Mimics Wagtail's Page model, converts YAML data to page attributes
  • MockStreamValue - Mimics StreamField, allows iteration over blocks
  • MockStreamBlock - Mimics individual content blocks with block_type and value
  • MockImage - Mimics Wagtail's Image model for the {% image %} tag

These mock objects provide the same API as real Wagtail objects, so templates work identically with both fixture data and real database content.

Features

Dashboard (/prototype/)

The dashboard automatically:

  • Scans all app template directories
  • Discovers .html files (excluding base templates and partials)
  • Shows which templates have fixtures
  • Displays fixture titles
  • Provides direct links to preview each template

Claude Code Skill for Model Generation

This repository includes a Claude Code skill (/wagtail-model-creator) that automates converting your YAML fixtures into production-ready Wagtail page models. The skill:

  • Analyzes your fixture keys and values
  • Determines appropriate field types (StreamField, CharField, ForeignKey, etc.)
  • Generates complete model code with imports and panels
  • Handles complex structures like StreamFields, StructBlocks, and nested data

To use it:

  1. Create and test your template with a fixture file
  2. Run /wagtail-model-creator in Claude Code
  3. Review and save the generated model code
  4. Run migrations to apply changes

Prototype Mode Indicator

When viewing a prototype, templates receive an is_prototype context variable. The base template (app/templates/base.html) uses this to display a prototype mode badge:

{% if is_prototype %}
<div class="prototype-badge">
    Prototype Mode | <a href="/prototype/">Dashboard</a>
</div>
{% endif %}

Production Safety

All prototype URLs only exist when DEBUG=True. In production:

  • /prototype/ routes don't exist
  • No prototype code is accessible
  • No security risk from development features

Tips and Best Practices

  1. Match your planned models - Structure fixtures to match the page models you'll create
  2. Use realistic data - Catch layout issues early with real-world content lengths
  3. Test edge cases - Long titles, missing images, empty content
  4. Organize by app - Keep fixtures in prototypes/fixtures/{app}/ matching template paths
  5. Frontend-first workflow - Design templates before writing models
  6. Iterate quickly - Change fixtures and refresh to see updates instantly
  7. Use placeholder images - Services like https://picsum.photos/ for quick mockups
  8. Use the model generator skill - If using Claude Code, leverage /wagtail-model-creator to automatically generate models from fixtures

Customization

Adding Custom Mock Block Types

To support custom block types, extend MockStreamBlock in prototypes/views.py:

class MockStreamBlock:
    def __init__(self, block_type, value):
        self.block_type = block_type

        # Add your custom block type handling
        if block_type == 'gallery':
            # Convert list of image URLs to MockImage objects
            self.value = [MockImage(img['url'], img.get('alt', '')) for img in value]
        elif block_type == 'accordion':
            # Handle accordion data structure
            self.value = value  # or transform as needed
        # ... existing code for image, embed, etc.
        else:
            self.value = value

Customizing the Dashboard

Edit prototypes/templates/prototypes/dashboard.html to:

  • Change styling and branding
  • Add filtering or search functionality
  • Show more fixture metadata
  • Add links to your documentation
  • Group templates differently

Filtering Templates

Update the dashboard() view to skip certain templates:

# Skip base templates, partials, and specific patterns
if (template_path.startswith('base') or
    '/_' in template_path or
    template_path.startswith('includes/') or
    'email' in template_path):
    continue

Troubleshooting

Templates not appearing in dashboard:

  • Verify templates are in {app}/templates/ directories
  • Check path configuration in dashboard() view
  • Ensure templates don't match skip patterns (base*, /_, etc.)
  • Check that app directories are being scanned

Fixture not loading:

  • Verify YAML syntax with a validator
  • Check fixture path matches template path exactly
  • Example: home/landing_page.htmlprototypes/fixtures/home/landing_page.yaml
  • Ensure PyYAML is installed: pip show PyYAML

Template rendering errors:

  • Check template syntax for Django/Wagtail tags
  • Verify fixture data structure matches template expectations
  • Look for typos in variable names
  • Check browser console and Django debug page for details

Images not displaying:

  • Use full URLs starting with http:// or https://
  • Verify URLs are publicly accessible
  • Check CORS and SSL for external images
  • Try placeholder service: https://picsum.photos/WIDTH/HEIGHT

Dashboard shows wrong path:

  • Adjust BASE_DIR paths in views.py
  • Check that settings.BASE_DIR points to your project root
  • Update both prototype_view() and dashboard() functions

Project Structure Reference

This repository structure:

wagtail-prototyper/
├── app/
│   ├── prototypes/          # ← Copy this entire folder
│   │   ├── __init__.py
│   │   ├── apps.py
│   │   ├── views.py         # Mock objects and view logic
│   │   ├── fixtures/        # YAML fixture files
│   │   │   ├── home/
│   │   │   │   └── home_page.yaml
│   │   │   └── core/
│   │   │       └── standard_page.yaml
│   │   └── templates/
│   │       └── prototypes/
│   │           └── dashboard.html
│   ├── home/
│   ├── core/
│   └── templates/
│       └── base.html        # Has prototype mode indicator
├── README.md                # This file
└── manage.py

Additional Resources

  • Example Templates: See app/home/templates/ and app/core/templates/ in this repo for working examples (these are examples from this demo project)
  • Example Fixtures: Check app/prototypes/fixtures/ in this repo for YAML structure examples (create your own for your project)
  • Base Template: Reference app/templates/base.html for prototype mode integration
  • Claude Code Skill: Use /wagtail-model-creator to automatically generate models from fixtures
  • Wagtail Docs: wagtail.org/documentation

Remember: The templates and fixtures in this repository are examples from the demo project. They show you how the system works, but you'll need to create your own templates and fixtures for your own project's pages.

License

This prototyping system can be freely used and modified for your projects.

About

A rapid template development system for Wagtail that allows you to build and iterate on templates using YAML fixture files before creating backend models.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors