A Django content management system with a user-friendly interface and powerful features for building websites and applications.
Core page functionality providing the foundation for all content in Wagtail. The Page model hierarchy enables tree-based content organization with powerful inheritance and customization capabilities.
The core Page model provides the foundation for all pages in Wagtail, including hierarchical structure, URL routing, publishing workflow, and content management.
class Page(
WorkflowMixin,
PreviewableMixin,
DraftStateMixin,
LockableMixin,
RevisionMixin,
TranslatableMixin,
SpecificMixin,
MP_Node,
index.Indexed,
ClusterableModel
):
"""
Base page model with full CMS functionality.
Core Fields:
title (str): Page title
draft_title (str): Draft version of title (auto-managed)
slug (str): URL slug for the page
content_type (ForeignKey): Content type for polymorphic behavior
url_path (str): Full URL path from site root (auto-managed)
seo_title (str): SEO-optimized title
search_description (str): Meta description for search engines
show_in_menus (bool): Whether page appears in navigation
owner (User): Page owner for permissions
Publishing Fields (from DraftStateMixin):
live (bool): Whether page is published and visible
has_unpublished_changes (bool): Whether there are unpublished changes
first_published_at (datetime): When page was first published
last_published_at (datetime): When page was last published
latest_revision_created_at (datetime): When latest revision was created
go_live_at (datetime): Scheduled publish time
expire_at (datetime): Scheduled expiration time
expired (bool): Whether page has expired
live_revision (ForeignKey): Currently live revision
Locking Fields (from LockableMixin):
locked (bool): Whether page is locked for editing
locked_by (User): User who locked the page
locked_at (datetime): When page was locked
Revision Fields (from RevisionMixin):
latest_revision (ForeignKey): Most recent revision
Translation Fields (from TranslatableMixin):
locale (ForeignKey): Language/locale for this page
translation_key (UUID): Links translations together
Tree Fields (from MP_Node):
path (str): Tree path for hierarchical queries
depth (int): Tree depth level
numchild (int): Number of direct children
Other Fields:
alias_of (ForeignKey): Original page if this is an alias
"""
# Core fields
title: str
draft_title: str
slug: str
content_type: ContentType
url_path: str
seo_title: str
search_description: str
show_in_menus: bool
owner: User
# Publishing state (DraftStateMixin)
live: bool
has_unpublished_changes: bool
first_published_at: datetime
last_published_at: datetime
latest_revision_created_at: datetime
go_live_at: datetime
expire_at: datetime
expired: bool
live_revision: Revision
# Locking (LockableMixin)
locked: bool
locked_by: User
locked_at: datetime
# Revisions (RevisionMixin)
latest_revision: Revision
# Translation (TranslatableMixin)
locale: Locale
translation_key: UUID
# Tree structure (MP_Node)
path: str
depth: int
numchild: int
# Aliases
alias_of: Page
# URL and routing methods
def get_url(self, request=None, current_site=None):
"""Get the URL for this page."""
def get_full_url(self, request=None):
"""Get the absolute URL including domain for this page."""
def route(self, request, path_components):
"""Route a request to this page or its descendants."""
def set_url_path(self, parent):
"""Update the url_path field based on this page's slug and parent."""
@classmethod
def route_for_request(cls, request, path):
"""Find the page to serve for the given request path."""
@classmethod
def find_for_request(cls, request, path):
"""Find page matching the request, returns (page, args, kwargs)."""
# Content serving methods
def serve(self, request):
"""Serve this page for the given request."""
def get_context(self, request):
"""Get template context for rendering this page."""
def get_template(self, request, *args, **kwargs):
"""Get template for rendering this page."""
# Admin interface methods
def get_admin_display_title(self):
"""Get title for display in admin interface."""
def get_admin_base_path(self):
"""Get the base path for admin URLs for this page."""
# Lifecycle methods
def save(self, *args, **kwargs):
"""Save the page, updating URL paths and search index."""
def delete(self):
"""Delete the page and all its descendants."""
def clean(self):
"""Validate the page before saving."""
# Content management methods
def copy(self, recursive=False, to=None, update_attrs=None, copy_revisions=True):
"""Create a copy of this page."""
def move(self, target, pos=None):
"""Move this page to a new location in the tree."""
def update_aliases(self, *, revision=None, _content_json=None, user=None):
"""Update any aliases of this page with new content."""
# Tree traversal methods
def get_children(self):
"""Get direct children of this page."""
def get_descendants(self, inclusive=False):
"""Get all descendants of this page."""
def get_ancestors(self, inclusive=False):
"""Get all ancestors of this page."""
def get_siblings(self, inclusive=False):
"""Get sibling pages at the same level."""
def is_site_root(self):
"""Check if this page is the root of a site."""
# Validation methods
@classmethod
def can_exist_under(cls, parent):
"""Check if this page type can be created under the parent."""
@classmethod
def can_create_at(cls, parent):
"""Check if this page type can be created under the parent."""
def can_move_to(self, parent):
"""Check if this page can be moved under the parent."""
@classmethod
def allowed_parent_page_models(cls):
"""Get allowed parent page models for this page type."""
@classmethod
def allowed_subpage_models(cls):
"""Get allowed subpage models under this page type."""
# Permission methods
def permissions_for_user(self, user):
"""Get permission tester for the given user."""
def get_view_restrictions(self):
"""Get view restrictions applying to this page."""
# Workflow methods
def get_workflow(self):
"""Get the workflow assigned to this page."""
# SEO and sitemap methods
def get_sitemap_urls(self, request=None):
"""Get URLs for XML sitemap generation."""
# Caching methods
def get_cache_key_components(self):
"""Get components for generating cache keys."""
# HTTP method validation
@classmethod
def allowed_http_method_names(cls):
"""Get allowed HTTP methods for this page type."""Mixins that provide specific functionality for custom models and pages.
class RevisionMixin:
"""
Adds revision capabilities to models.
Methods provide version control and content history.
"""
def save_revision(self, user=None, approved_go_live_at=None, changed=True, log_action=False, previous_revision=None, clean=True):
"""
Save a new revision of this object.
Parameters:
user: User who made the changes
approved_go_live_at: When changes should go live
changed: Whether content has changed
log_action: Whether to log this action
previous_revision: Previous revision for comparison
clean: Whether to clean the object before saving
"""
def get_latest_revision(self):
"""Get the most recent revision."""
def get_latest_revision_as_object(self):
"""Get the content of the latest revision as an object."""
class DraftStateMixin:
"""
Adds draft/live state management to models.
Provides publishing workflow capabilities.
"""
live: bool
has_unpublished_changes: bool
first_published_at: datetime
last_published_at: datetime
go_live_at: datetime
expire_at: datetime
expired: bool
live_revision: Revision
def publish(self, revision=None, user=None):
"""Publish this content, making it live."""
def unpublish(self, set_expired=False, commit=True, user=None):
"""Remove this content from live site."""
def get_latest_revision_as_object(self):
"""Get latest revision content as object (overrides RevisionMixin)."""
def get_scheduled_revision_as_object(self):
"""Get scheduled revision if one exists."""
@property
def status_string(self):
"""Get human-readable status for admin display."""
class LockableMixin:
"""
Adds content locking functionality to prevent concurrent editing.
"""
locked: bool
locked_by: User
locked_at: datetime
def get_lock(self):
"""Get lock object if this content is locked."""
def with_content_json(self, content):
"""Return a copy with the given content restored."""
class WorkflowMixin:
"""
Adds workflow support for approval processes.
"""
@property
def current_workflow_task_state(self):
"""Get current workflow task state."""
@property
def current_workflow_task(self):
"""Get current workflow task."""
@property
def workflow_states(self):
"""Get all workflow states for this content."""
@classmethod
def get_default_workflow(cls):
"""Get default workflow for this content type."""
def get_workflow(self):
"""Get the workflow assigned to this content."""
class PreviewableMixin:
"""
Adds preview functionality for draft content.
"""
@property
def preview_modes(self):
"""Get available preview modes."""
@property
def preview_sizes(self):
"""Get available preview sizes."""
def serve_preview(self, request, mode_name):
"""Serve a preview of this content."""
def make_preview_request(self, request=None, mode_name=''):
"""Create a fake request for previewing content."""
def get_preview_context(self, request, mode_name):
"""Get context for preview rendering."""
def get_preview_template(self, request, mode_name):
"""Get template for preview rendering."""
def is_previewable(self):
"""Check if this content can be previewed."""
class TranslatableMixin:
"""
Adds translation support for multi-language content.
"""
locale: Locale
translation_key: UUID
def get_translations(self, inclusive=False):
"""Get all translations of this content."""
def copy_for_translation(self, locale):
"""Create a copy for translation to another locale."""Models for managing content lifecycle, organization, and access control.
class Revision:
"""
Represents a saved version of page or model content.
"""
content_object: Model
base_content_type: ContentType
object_str: str
created_at: datetime
user: User
approved_go_live_at: datetime
def as_object(self):
"""Return the content of this revision as a model instance."""
def publish(self):
"""Publish this revision, making it live."""
def is_latest_revision(self):
"""Check if this is the latest revision."""
def get_previous(self):
"""Get the previous revision."""
def get_next(self):
"""Get the next revision."""
class Collection:
"""
Organizes media and content with hierarchical permissions.
"""
name: str
path: str
def get_descendants(self, inclusive=False):
"""Get all descendant collections."""
def get_ancestors(self, inclusive=False):
"""Get all ancestor collections."""
def get_view_restrictions(self):
"""Get view restrictions for this collection."""
class Site:
"""
Represents a website with its own domain and root page.
"""
hostname: str
port: int
site_name: str
root_page: Page
root_url: str
@classmethod
def find_for_request(cls, request):
"""Find the Site object for the given HTTP request."""
@classmethod
def get_site_root_paths(cls):
"""Get URL paths for all site root pages."""
class Locale:
"""
Represents a language/region combination for internationalization.
"""
language_code: str
region_code: str
@classmethod
def get_default(cls):
"""Get the default locale for the site."""
@classmethod
def get_active(cls):
"""Get all active locales."""Models for managing user permissions and access control.
class GroupPagePermission:
"""
Assigns page permissions to user groups.
"""
group: Group
page: Page
permission: Permission
objects: GroupPagePermissionManager
class PageViewRestriction:
"""
Restricts page viewing to specific users or groups.
"""
page: Page
restriction_type: str # 'password', 'groups', 'login'
password: str
groups: ManyToManyField[Group]
def save(self, **kwargs):
"""Save with audit logging."""
def delete(self, **kwargs):
"""Delete with audit logging."""
class PagePermissionTester:
"""
Utility class for testing user permissions on pages.
"""
def __init__(self, user, page):
"""Initialize with user and page to test."""
def can_edit(self):
"""Check if user can edit the page."""
def can_delete(self):
"""Check if user can delete the page."""
def can_publish(self):
"""Check if user can publish the page."""
def can_unpublish(self):
"""Check if user can unpublish the page."""from wagtail.models import Page
from wagtail.fields import RichTextField
from wagtail.admin.panels import FieldPanel
from django.db import models
class BlogPage(Page):
"""Custom page model for blog posts."""
date = models.DateField("Post date")
intro = models.CharField(max_length=250)
body = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('date'),
FieldPanel('intro'),
FieldPanel('body'),
]
def get_context(self, request):
context = super().get_context(request)
context['recent_posts'] = BlogPage.objects.live().order_by('-date')[:5]
return context# Get all pages under a section
section_page = Page.objects.get(slug='news')
news_articles = section_page.get_children().live().order_by('-first_published_at')
# Move a page to a new parent
page_to_move = Page.objects.get(slug='old-location')
new_parent = Page.objects.get(slug='new-section')
page_to_move.move(new_parent, pos='last-child')
# Copy a page with all its content
original_page = Page.objects.get(slug='template-page')
copied_page = original_page.copy(
recursive=True, # Copy child pages too
update_attrs={'title': 'New Page Title', 'slug': 'new-page-slug'}
)from wagtail.models import RevisionMixin, DraftStateMixin
from django.db import models
class Article(RevisionMixin, DraftStateMixin, models.Model):
"""Custom model with revision and publishing capabilities."""
title = models.CharField(max_length=255)
content = models.TextField()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# Save revision after saving the model
self.save_revision()
def go_live(self):
"""Publish this article."""
self.live = True
self.save()
self.publish()Install with Tessl CLI
npx tessl i tessl/pypi-wagtail