CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-wagtail

A Django content management system with a user-friendly interface and powerful features for building websites and applications.

Overview
Eval results
Files

contrib.mddocs/

Contrib Modules and Extensions

Additional functionality modules that extend Wagtail's core capabilities with forms, redirects, settings management, and specialized content types. These modules provide common functionality needed in most Wagtail sites.

Capabilities

Forms Module

Form handling functionality for creating contact forms, surveys, and data collection pages.

class AbstractForm(Page):
    """
    Base class for form pages with submission handling.
    
    Properties:
        thank_you_text (RichTextField): Message shown after successful submission
        from_address (str): Default sender email address
        to_address (str): Email address to send submissions to
        subject (str): Email subject line
    """
    thank_you_text: RichTextField
    from_address: str
    to_address: str
    subject: str
    
    def get_form_class(self):
        """Get the form class for this form page."""
    
    def get_form(self, *args, **kwargs):
        """Get form instance with data."""
    
    def process_form_submission(self, form):
        """Process valid form submission and return form submission object."""
    
    def render_landing_page(self, request, form_submission=None, *args, **kwargs):
        """Render thank you page after form submission."""

class AbstractFormField(Orderable):
    """
    Individual field definition for forms.
    
    Properties:
        label (str): Field label text
        field_type (str): Type of form field ('singleline', 'multiline', 'email', etc.)
        required (bool): Whether field is required
        choices (str): Choices for dropdown/radio fields (newline separated)
        default_value (str): Default field value
        help_text (str): Help text for the field
    """
    label: str
    field_type: str
    required: bool
    choices: str
    default_value: str
    help_text: str
    
    @property
    def clean_name(self):
        """Get cleaned field name for use in forms."""

class AbstractEmailForm(AbstractForm):
    """
    Form that emails submissions to specified addresses.
    """
    def process_form_submission(self, form):
        """Process submission and send email notification."""

class AbstractFormSubmission(models.Model):
    """
    Stores form submission data.
    
    Properties:
        form_data (JSONField): Submitted form data
        submit_time (datetime): When form was submitted
    """
    form_data: JSONField
    submit_time: datetime
    
    def get_data(self):
        """Get form data as dictionary."""

Redirects Module

URL redirect management for handling moved content and SEO.

class Redirect(models.Model):
    """
    URL redirect configuration.
    
    Properties:
        old_path (str): Original URL path to redirect from
        site (Site): Site this redirect applies to
        is_permanent (bool): Whether redirect is permanent (301) or temporary (302)
        redirect_page (Page): Page to redirect to (optional)
        redirect_link (str): External URL to redirect to (optional)
    """
    old_path: str
    site: Site
    is_permanent: bool
    redirect_page: Page
    redirect_link: str
    
    def get_is_permanent(self):
        """Check if this is a permanent redirect."""
    
    @property
    def redirect_to(self):
        """Get the target URL for this redirect."""
    
    @classmethod
    def get_for_site(cls, site=None):
        """Get all redirects for a specific site."""

Settings Module

Site-specific settings management with admin interface integration.

class BaseSiteSetting(models.Model):
    """
    Base class for site-specific settings.
    
    Properties:
        site (Site): Site these settings apply to
    """
    site: Site
    
    class Meta:
        abstract = True
    
    @classmethod
    def for_site(cls, site):
        """Get settings instance for a specific site."""

def register_setting(model=None, **kwargs):
    """
    Decorator to register a model as a site setting.
    
    Usage:
    @register_setting
    class SocialMediaSettings(BaseSiteSetting):
        facebook_url = models.URLField(blank=True)
    
    Parameters:
        model (Model): Model class to register
        icon (str): Icon name for admin interface
    """

class SettingsMenuItem:
    """Menu item for settings in admin interface."""
    
    def __init__(self, model, icon='cog', **kwargs):
        """Initialize settings menu item."""

Sitemaps Module

XML sitemap generation for search engine optimization.

def sitemap(request):
    """
    Generate XML sitemap for all live pages.
    
    Returns:
        HttpResponse: XML sitemap response
    """

class Sitemap:
    """
    Sitemap configuration class.
    
    Customize sitemap generation by subclassing.
    """
    def items(self):
        """Get items to include in sitemap."""
    
    def location(self, item):
        """Get URL for sitemap item."""
    
    def lastmod(self, item):
        """Get last modification date for item."""
    
    def changefreq(self, item):
        """Get change frequency for item."""
    
    def priority(self, item):
        """Get priority for item."""

Routable Page Module

Custom URL routing within page hierarchies for dynamic content.

class RoutablePageMixin:
    """
    Mixin that adds custom URL routing to pages.
    
    Allows pages to handle multiple URL patterns and views.
    """
    def serve(self, request, *args, **kwargs):
        """Enhanced serve method with route handling."""
    
    def reverse_subpage(self, name, args=None, kwargs=None):
        """Get URL for a named sub-route."""

def route(pattern, name=None):
    """
    Decorator to define custom routes within a page.
    
    Parameters:
        pattern (str): URL regex pattern
        name (str): Optional name for the route
    
    Usage:
    class BlogPage(RoutablePageMixin, Page):
        @route(r'^archive/(\d{4})/$', name='archive_year')
        def archive_year(self, request, year):
            return render(request, 'blog/archive.html', {'year': year})
    """

Table Block Module

Editable table content blocks for structured data presentation.

class TableBlock(StructBlock):
    """
    Block for creating editable tables in StreamField.
    
    Properties:
        table_options (dict): Configuration options for table editing
    """
    def __init__(self, required=True, help_text=None, table_options=None, **kwargs):
        """
        Initialize table block.
        
        Parameters:
            table_options (dict): Options like row/column headers, cell types
        """
    
    def render(self, value, context=None):
        """Render table as HTML."""

class TypedTableBlock(TableBlock):
    """
    Table block with column type definitions for structured data.
    
    Parameters:
        columns (list): List of column definitions with types
    """
    def __init__(self, columns, **kwargs):
        """
        Initialize typed table block.
        
        Parameters:
            columns (list): List of (name, block_type) tuples
        """

Snippets System

Registration and management of reusable content snippets.

def register_snippet(model):
    """
    Decorator to register a model as a snippet for admin interface.
    
    Usage:
    @register_snippet
    class Category(models.Model):
        name = models.CharField(max_length=100)
    
    Parameters:
        model (Model): Django model to register as snippet
    """

class SnippetViewSet(ModelViewSet):
    """
    ViewSet for managing snippets in admin interface.
    
    Provides CRUD operations for snippet models.
    """
    def get_queryset(self):
        """Get queryset for snippet listing."""

class SnippetChooserBlock(ChooserBlock):
    """
    Block for choosing snippets in StreamField.
    
    Parameters:
        target_model (Model): Snippet model to choose from
    """
    def __init__(self, target_model, **kwargs):
        """Initialize snippet chooser block."""

Search Promotions Module

Promoted search results for editorial control over search rankings.

class SearchPromotion(models.Model):
    """
    Promoted search result for specific queries.
    
    Properties:
        query (str): Search query to promote results for
        page (Page): Page to promote in results
        sort_order (int): Order of promotion in results
        description (str): Custom description for promoted result
    """
    query: str
    page: Page
    sort_order: int
    description: str
    
    @classmethod
    def get_for_query(cls, query_string):
        """Get promotions matching a search query."""

Frontend Cache Module

Cache invalidation for CDN and reverse proxy integration.

def purge_page_from_cache(page, backend_settings=None, backends=None):
    """
    Purge a specific page from frontend caches.
    
    Parameters:
        page (Page): Page to purge from cache
        backend_settings (dict): Cache backend configuration
        backends (list): List of cache backends to purge from
    """

def purge_pages_from_cache(pages, backend_settings=None, backends=None):
    """
    Purge multiple pages from frontend caches.
    
    Parameters:
        pages (QuerySet): Pages to purge from cache
        backend_settings (dict): Cache backend configuration
        backends (list): List of cache backends to purge from
    """

class CloudflareBackend:
    """Cache backend for Cloudflare CDN integration."""
    
    def __init__(self, config):
        """Initialize Cloudflare backend with API credentials."""
    
    def purge(self, urls):
        """Purge specific URLs from Cloudflare cache."""

class CloudfrontBackend:
    """Cache backend for AWS CloudFront CDN integration."""
    
    def __init__(self, config):
        """Initialize CloudFront backend with AWS credentials."""
    
    def purge(self, urls):
        """Create CloudFront invalidation for URLs."""

Usage Examples

Creating Form Pages

from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField
from wagtail.contrib.forms.panels import FormSubmissionsPanel
from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel
from modelcluster.fields import ParentalKey

class FormField(AbstractFormField):
    """Custom form field with page relationship."""
    page = ParentalKey('ContactPage', on_delete=models.CASCADE, related_name='form_fields')

class ContactPage(AbstractEmailForm):
    """Contact form page with email notifications."""
    intro = RichTextField(blank=True)
    thank_you_text = RichTextField(blank=True)
    
    content_panels = AbstractEmailForm.content_panels + [
        FieldPanel('intro'),
        InlinePanel('form_fields', label="Form fields"),
        FieldPanel('thank_you_text'),
        MultiFieldPanel([
            FieldPanel('to_address', classname="col6"),
            FieldPanel('from_address', classname="col6"),
            FieldPanel('subject'),
        ], "Email Settings"),
    ]
    
    def get_form_fields(self):
        return self.form_fields.all()

# In templates
{% extends "base.html" %}
{% load wagtailcore_tags %}

{% block content %}
    <div class="contact-page">
        <h1>{{ page.title }}</h1>
        {{ page.intro|richtext }}
        
        <form action="{% pageurl page %}" method="post">
            {% csrf_token %}
            {% for field in form %}
                <div class="field">
                    {{ field.label_tag }}
                    {{ field }}
                    {{ field.errors }}
                </div>
            {% endfor %}
            <button type="submit">Send Message</button>
        </form>
    </div>
{% endblock %}

Site Settings

from wagtail.contrib.settings.models import BaseSiteSetting, register_setting
from wagtail.admin.panels import FieldPanel

@register_setting
class SocialMediaSettings(BaseSiteSetting):
    """Social media links and API keys."""
    facebook_url = models.URLField(blank=True, help_text='Facebook page URL')
    twitter_handle = models.CharField(max_length=100, blank=True)
    instagram_url = models.URLField(blank=True)
    google_analytics_id = models.CharField(max_length=100, blank=True)
    
    panels = [
        MultiFieldPanel([
            FieldPanel('facebook_url'),
            FieldPanel('twitter_handle'),
            FieldPanel('instagram_url'),
        ], heading='Social Media Links'),
        FieldPanel('google_analytics_id'),
    ]

# Usage in templates
{% load wagtailsettings_tags %}
{% get_settings "myapp.SocialMediaSettings" as social_settings %}

<footer>
    {% if social_settings.facebook_url %}
        <a href="{{ social_settings.facebook_url }}">Facebook</a>
    {% endif %}
    {% if social_settings.twitter_handle %}
        <a href="https://twitter.com/{{ social_settings.twitter_handle }}">Twitter</a>
    {% endif %}
</footer>

Routable Pages

from wagtail.contrib.routable_page.models import RoutablePageMixin, route
from django.shortcuts import render

class BlogIndexPage(RoutablePageMixin, Page):
    """Blog index with custom routing for archives and tags."""
    
    def get_context(self, request):
        context = super().get_context(request)
        context['blog_posts'] = BlogPage.objects.child_of(self).live()
        return context
    
    @route(r'^$')
    def blog_index(self, request):
        """Default blog listing."""
        return self.serve(request)
    
    @route(r'^archive/(\d{4})/$', name='archive_year')
    def archive_year(self, request, year):
        """Blog posts for specific year."""
        posts = BlogPage.objects.child_of(self).live().filter(date__year=year)
        return render(request, 'blog/archive.html', {
            'page': self,
            'posts': posts,
            'year': year,
        })
    
    @route(r'^tag/([\w-]+)/$', name='tag')
    def tag_view(self, request, tag_slug):
        """Blog posts with specific tag."""
        posts = BlogPage.objects.child_of(self).live().filter(tags__slug=tag_slug)
        return render(request, 'blog/tag.html', {
            'page': self,
            'posts': posts,
            'tag': tag_slug,
        })
    
    def get_sitemap_urls(self, request=None):
        """Add custom routes to sitemap."""
        sitemap = super().get_sitemap_urls(request)
        
        # Add yearly archives
        years = BlogPage.objects.dates('date', 'year')
        for year in years:
            sitemap.append({
                'location': self.reverse_subpage('archive_year', args=[year.year]),
                'lastmod': BlogPage.objects.filter(date__year=year.year).latest('last_published_at').last_published_at,
            })
        
        return sitemap

# Template usage
<nav class="blog-nav">
    <a href="{% pageurl page %}">All Posts</a>
    <a href="{% pageurl page %}archive/2023/">2023 Archive</a>
    <a href="{% pageurl page %}tag/django/">Django Posts</a>
</nav>

Snippets Usage

from wagtail.snippets.models import register_snippet
from wagtail.admin.panels import FieldPanel
from wagtail.search import index

@register_snippet
class Category(models.Model):
    """Blog category snippet."""
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)
    description = models.TextField(blank=True)
    icon = models.CharField(max_length=50, blank=True)
    
    panels = [
        FieldPanel('name'),
        FieldPanel('slug'),
        FieldPanel('description'),
        FieldPanel('icon'),
    ]
    
    search_fields = [
        index.SearchField('name'),
        index.SearchField('description'),
    ]
    
    def __str__(self):
        return self.name
    
    class Meta:
        ordering = ['name']

@register_snippet
class Author(models.Model):
    """Author snippet with contact information."""
    name = models.CharField(max_length=100)
    bio = models.TextField()
    photo = models.ForeignKey('wagtailimages.Image', on_delete=models.SET_NULL, null=True, blank=True)
    email = models.EmailField(blank=True)
    website = models.URLField(blank=True)
    
    panels = [
        FieldPanel('name'),
        FieldPanel('bio'),
        FieldPanel('photo'),
        FieldPanel('email'),
        FieldPanel('website'),
    ]
    
    def __str__(self):
        return self.name

# Using snippets in pages
class BlogPage(Page):
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
    author = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True, blank=True)
    
    content_panels = Page.content_panels + [
        FieldPanel('category'),
        FieldPanel('author'),
    ]

# In StreamField blocks
from wagtail.snippets.blocks import SnippetChooserBlock

class ContentBlock(StructBlock):
    heading = CharBlock()
    text = RichTextBlock()
    category = SnippetChooserBlock(Category, required=False)

Table Blocks

from wagtail.contrib.table_block.blocks import TableBlock

class ContentPage(Page):
    """Page with table support."""
    body = StreamField([
        ('paragraph', RichTextBlock()),
        ('table', TableBlock(table_options={
            'minSpareRows': 0,
            'startRows': 4,
            'startCols': 4,
            'colHeaders': False,
            'rowHeaders': False,
            'contextMenu': True,
            'editor': 'text',
            'stretchH': 'all',
            'height': 216,
            'language': 'en',
            'renderer': 'text',
            'autoColumnSize': False,
        })),
    ])
    
    content_panels = Page.content_panels + [
        FieldPanel('body'),
    ]

# Custom table template (templates/table_block.html)
<table class="data-table">
    {% for row in self.data %}
    <tr>
        {% for cell in row %}
        <td>{{ cell }}</td>
        {% endfor %}
    </tr>
    {% endfor %}
</table>

Install with Tessl CLI

npx tessl i tessl/pypi-wagtail

docs

admin-interface.md

api.md

content-fields.md

contrib.md

index.md

media.md

page-models.md

search.md

system-integration.md

templates.md

workflows.md

tile.json