A Django content management system with a user-friendly interface and powerful features for building websites and applications.
Customizable admin interface with panel system for organizing edit forms, menu configuration, and extensible admin UI components. Wagtail's admin interface provides a user-friendly content management experience with powerful customization capabilities.
Panel classes for organizing edit forms and controlling the admin interface layout.
class Panel:
"""
Base panel class for edit forms.
All panel types inherit from this base class.
"""
def __init__(self, heading='', classname='', help_text='', **kwargs):
"""
Initialize panel with display options.
Parameters:
heading (str): Panel heading text
classname (str): CSS class for styling
help_text (str): Help text displayed to users
"""
class FieldPanel(Panel):
"""
Panel for editing a single model field.
The most commonly used panel type for basic field editing.
"""
def __init__(self, field_name, widget=None, heading='', classname='', help_text='', **kwargs):
"""
Initialize field panel.
Parameters:
field_name (str): Name of the model field to edit
widget (Widget): Custom form widget to use
heading (str): Custom heading for the field
classname (str): CSS class for styling
help_text (str): Help text for the field
"""
class MultiFieldPanel(Panel):
"""
Panel that groups multiple fields together under a common heading.
"""
def __init__(self, children, heading='', classname='', help_text='', **kwargs):
"""
Initialize multi-field panel.
Parameters:
children (list): List of child panels to group
heading (str): Heading for the group
classname (str): CSS class for styling
help_text (str): Help text for the group
"""
class TitleFieldPanel(FieldPanel):
"""
Special panel for title fields that enables automatic slug generation.
"""
def __init__(self, field_name, **kwargs):
"""Initialize title field panel with slug generation."""
class PublishingPanel(Panel):
"""
Panel that displays publishing workflow controls and status.
"""
def __init__(self, **kwargs):
"""Initialize publishing panel."""
class CommentPanel(Panel):
"""
Panel that displays the comments interface for collaborative editing.
"""
def __init__(self, **kwargs):
"""Initialize comment panel."""
class InlinePanel(Panel):
"""
Panel for editing related objects inline within the parent object's form.
"""
def __init__(self, relation_name, panels=None, heading='', label='', min_num=None, max_num=None, classname='', help_text='', **kwargs):
"""
Initialize inline panel.
Parameters:
relation_name (str): Name of the foreign key relation
panels (list): List of panels for editing related objects
heading (str): Panel heading
label (str): Label for individual items
min_num (int): Minimum number of items required
max_num (int): Maximum number of items allowed
classname (str): CSS class for styling
help_text (str): Help text for the panel
"""
class ObjectList(Panel):
"""
Container for organizing multiple panels into logical groups.
"""
def __init__(self, children, heading='', classname='', **kwargs):
"""
Initialize object list.
Parameters:
children (list): List of child panels
heading (str): Group heading
classname (str): CSS class for styling
"""
class TabbedInterface(Panel):
"""
Creates a tabbed interface with multiple panel groups.
"""
def __init__(self, children, **kwargs):
"""
Initialize tabbed interface.
Parameters:
children (list): List of ObjectList panels for each tab
"""Classes for customizing the admin navigation menu.
class MenuItem:
"""
Individual menu item in the admin interface.
"""
def __init__(self, label, url, name=None, icon_name=None, attrs=None, order=1000, **kwargs):
"""
Initialize menu item.
Parameters:
label (str): Display text for the menu item
url (str): URL the menu item links to
name (str): Unique identifier for the menu item
icon_name (str): Icon to display with the menu item
attrs (dict): Additional HTML attributes
order (int): Sort order for menu positioning
"""
class Menu:
"""
Container for organizing menu items.
"""
def __init__(self, register_hook_name=None, construct_hook_name=None):
"""Initialize menu container."""
def register_menu_item(self, menu_item):
"""Register a menu item with this menu."""
class SubmenuMenuItem(MenuItem):
"""
Menu item that contains nested menu items.
"""
def __init__(self, label, menu, **kwargs):
"""
Initialize submenu item.
Parameters:
label (str): Display text for the submenu
menu (Menu): Menu object containing child items
"""ViewSet classes for creating complete admin interfaces for models.
class ViewSet:
"""
Base viewset class providing a collection of views for a specific functionality.
"""
name: str
url_prefix: str
def get_urlpatterns(self):
"""Get URL patterns for this viewset."""
def get_admin_menu_item(self):
"""Get menu item for admin navigation."""
class ModelViewSet(ViewSet):
"""
Complete CRUD interface for Django models.
Provides list, create, edit, delete, and inspect views.
"""
model: Model
form_class: ModelForm
index_template_name: str
create_template_name: str
edit_template_name: str
delete_template_name: str
def __init__(self, name, model=None, **kwargs):
"""
Initialize model viewset.
Parameters:
name (str): Unique name for the viewset
model (Model): Django model to manage
"""
def get_queryset(self, request):
"""Get the queryset for listing objects."""
def get_form_class(self, for_update=False):
"""Get the form class for creating/editing objects."""
def get_edit_handler(self):
"""Get the edit handler (panel configuration) for forms."""
class PageViewSet(ModelViewSet):
"""
Specialized viewset for page models with additional page-specific functionality.
"""
template_name: str
def get_edit_handler(self):
"""Get edit handler with page-specific panels."""Form classes and widgets for the admin interface.
class WagtailAdminModelForm(forms.ModelForm):
"""
Base form class for Wagtail admin forms.
Provides consistent styling and functionality.
"""
def __init__(self, *args, **kwargs):
"""Initialize admin form with Wagtail styling."""
class WagtailAdminPageForm(WagtailAdminModelForm):
"""
Form class specifically for page editing with additional page functionality.
"""
def clean(self):
"""Validate page-specific constraints."""
# Widget classes for form fields
class AdminAutoHeightTextInput(forms.Textarea):
"""Text input that automatically adjusts height."""
class AdminPageChooser(forms.Select):
"""Widget for choosing pages with search interface."""
class AdminImageChooser(forms.ClearableFileInput):
"""Widget for choosing images with preview."""
class AdminDocumentChooser(forms.ClearableFileInput):
"""Widget for choosing documents with metadata."""
class AdminTagWidget(forms.TextInput):
"""Widget for tag input with autocomplete."""
class AdminDateInput(forms.DateInput):
"""Date input widget with calendar picker."""
class AdminTimeInput(forms.TimeInput):
"""Time input widget with time picker."""
class AdminDateTimeInput(forms.DateTimeInput):
"""DateTime input widget with combined picker."""Classes for managing admin permissions and access control.
class PermissionHelper:
"""
Helper class for checking permissions in admin views.
"""
def __init__(self, model, inspect_view_enabled=False):
"""
Initialize permission helper.
Parameters:
model (Model): Model to check permissions for
inspect_view_enabled (bool): Whether inspect view is available
"""
def user_can_list(self, user):
"""Check if user can view the listing page."""
def user_can_create(self, user):
"""Check if user can create new objects."""
def user_can_inspect_obj(self, user, obj):
"""Check if user can inspect a specific object."""
def user_can_edit_obj(self, user, obj):
"""Check if user can edit a specific object."""
def user_can_delete_obj(self, user, obj):
"""Check if user can delete a specific object."""
class PagePermissionHelper(PermissionHelper):
"""
Permission helper specifically for page models with page-specific permissions.
"""
def user_can_edit_obj(self, user, obj):
"""Check page-specific edit permissions."""from wagtail.models import Page
from wagtail.fields import RichTextField, StreamField
from wagtail.admin.panels import (
FieldPanel, MultiFieldPanel, InlinePanel, TabbedInterface, ObjectList
)
from django.db import models
class BlogPage(Page):
"""Blog page with custom admin interface."""
date = models.DateField("Post date")
intro = models.CharField(max_length=250)
body = RichTextField(blank=True)
featured_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
# Organize fields into logical groups
content_panels = Page.content_panels + [
MultiFieldPanel([
FieldPanel('date'),
FieldPanel('intro'),
], heading="Article Information"),
FieldPanel('body'),
FieldPanel('featured_image'),
]
# Create tabbed interface
edit_handler = TabbedInterface([
ObjectList(content_panels, heading='Content'),
ObjectList(Page.promote_panels, heading='Promote'),
ObjectList(Page.settings_panels, heading='Settings'),
])from wagtail.admin.viewsets import ModelViewSet
from wagtail.admin.ui.tables import Column, DateColumn
from django.db import models
class EventPage(Page):
"""Event page model."""
event_date = models.DateField()
location = models.CharField(max_length=255)
capacity = models.IntegerField()
class EventPageViewSet(ModelViewSet):
"""Custom admin interface for events."""
model = EventPage
icon = 'date'
menu_label = 'Events'
menu_order = 200
add_to_settings_menu = False
# Customize the listing page
list_display = ['title', 'event_date', 'location', 'capacity', 'live']
list_filter = ['event_date', 'live']
search_fields = ['title', 'location']
# Custom columns for the listing
def get_columns(self):
return [
Column('title', label='Event Title', sort_key='title'),
DateColumn('event_date', label='Date', sort_key='event_date'),
Column('location', label='Location'),
Column('capacity', label='Capacity'),
]
# Register the viewset
event_viewset = EventPageViewSet('events')from wagtail import hooks
from wagtail.admin.menu import MenuItem, Menu, SubmenuMenuItem
from django.urls import reverse, path
from django.http import HttpResponse
@hooks.register('register_admin_urls')
def register_admin_urls():
"""Register custom admin URLs."""
return [
path('reports/', my_reports_view, name='my_reports'),
path('tools/', my_tools_view, name='my_tools'),
]
def my_reports_view(request):
"""Custom reports view."""
return HttpResponse('<h1>Reports</h1>')
def my_tools_view(request):
"""Custom tools view."""
return HttpResponse('<h1>Tools</h1>')
@hooks.register('construct_main_menu')
def add_reports_menu_item(request, menu_items):
"""Add reports to main menu."""
menu_items.append(
MenuItem(
'Reports',
reverse('my_reports'),
icon_name='doc-full-inverse',
order=300
)
)
@hooks.register('construct_main_menu')
def add_tools_submenu(request, menu_items):
"""Add tools submenu."""
tools_menu = Menu()
tools_menu.register_menu_item(
MenuItem('Tool 1', reverse('my_tools'), icon_name='cog')
)
menu_items.append(
SubmenuMenuItem(
'Tools',
tools_menu,
icon_name='cogs',
order=400
)
)from wagtail.admin.widgets import AdminPageChooser, AdminImageChooser
from wagtail.admin.panels import FieldPanel
from django import forms
from django.db import models
class CustomWidget(forms.TextInput):
"""Custom admin widget with special functionality."""
template_name = 'admin/widgets/custom_widget.html'
class Media:
css = {'all': ('css/custom-widget.css',)}
js = ('js/custom-widget.js',)
class LandingPage(Page):
"""Landing page with custom form widgets."""
related_page = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL
)
hero_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL
)
special_field = models.CharField(max_length=100)
content_panels = Page.content_panels + [
FieldPanel('related_page', widget=AdminPageChooser(page_type='blog.BlogPage')),
FieldPanel('hero_image', widget=AdminImageChooser()),
FieldPanel('special_field', widget=CustomWidget()),
]from wagtail.models import Orderable
from modelcluster.fields import ParentalKey
class GalleryImage(Orderable):
"""Individual image in a gallery."""
page = ParentalKey('GalleryPage', related_name='gallery_images', on_delete=models.CASCADE)
image = models.ForeignKey('wagtailimages.Image', on_delete=models.CASCADE)
caption = models.CharField(max_length=250, blank=True)
panels = [
FieldPanel('image'),
FieldPanel('caption'),
]
class GalleryPage(Page):
"""Page with inline image gallery editing."""
intro = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('intro'),
InlinePanel(
'gallery_images',
label="Gallery Images",
min_num=1,
max_num=20,
heading="Image Gallery"
),
]Install with Tessl CLI
npx tessl i tessl/pypi-wagtail