Sage Abdullah
Sage Abdullah
An open source content management system (CMS) built on Django.
Itβs built around the Page
content type, which is stored in a tree structure that defines the siteβs URLs.
Wagtail gives you the power to:
Page
ββ Home βββ Publications βββ My latest publication βββ Gallery βββ About Us βββ Team βββ Careers
ββ Home βββ Publications βββ My latest publication βββ Gallery βββ About Us βββ Team βββ Careers
/ /publications/ /publications/my-latest-publication/ /gallery/ /about/ /about/team/ /about/careers/
/ /publications/ /publications/my-latest-publication/ /gallery/ /about/ /about/team/ /about/careers/
You may have other "snippets" of content that are not part of the page tree.
Or, content types from a project that does not use a CMS.
from django.db import models from wagtail.snippets.models import register_snippet @register_snippet class Person(models.Model): name = models.CharField(max_length=255) intro = models.TextField() ...
from django.db import models from wagtail.snippets.models import register_snippet @register_snippet class Person(models.Model): name = models.CharField(max_length=255) intro = models.TextField() ...
Developers have complete control over the content types and their fields β in the code.
class BlogPage(Page): introduction = models.TextField(help_text="Text to describe the page", blank=True) image = models.ForeignKey("wagtailimages.Image", ...) body = StreamField(BaseStreamBlock(), verbose_name="Page body", ...) subtitle = models.CharField(blank=True, max_length=255) author = models.ForeignKey("myapp.Person", ...) ... @register_snippet class Person(models.Model): name = models.CharField(max_length=255) intro = models.TextField() ...
class BlogPage(Page): introduction = models.TextField(help_text="Text to describe the page", blank=True) image = models.ForeignKey("wagtailimages.Image", ...) body = StreamField(BaseStreamBlock(), verbose_name="Page body", ...) subtitle = models.CharField(blank=True, max_length=255) author = models.ForeignKey("myapp.Person", ...) ... @register_snippet class Person(models.Model): name = models.CharField(max_length=255) intro = models.TextField() ...
To help editors see how their changes will look on the live site, developers can define preview modes.
from wagtail.models import PreviewableMixin @register_snippet class Product(PreviewableMixin, models.Model): preview_modes = [ ("index", "Index"), ("detail", "Detail"), ] def get_preview_template(self, request, preview_mode): return f"products/{preview_mode}.html" def get_preview_context(self, request, preview_mode): if preview_mode == "index": return {"products": [self]*20} return {"product": self}
from wagtail.models import PreviewableMixin @register_snippet class Product(PreviewableMixin, models.Model): preview_modes = [ ("index", "Index"), ("detail", "Detail"), ] def get_preview_template(self, request, preview_mode): return f"products/{preview_mode}.html" def get_preview_context(self, request, preview_mode): if preview_mode == "index": return {"products": [self]*20} return {"product": self}
To help editors see how their changes will look on the live site, developers can define preview modes.
from wagtail.models import PreviewableMixin @register_snippet class Product(PreviewableMixin, models.Model): preview_modes = [ ("index", "Index"), ("detail", "Detail"), ] def get_preview_template(self, request, preview_mode): return f"products/{preview_mode}.html" def get_preview_context(self, request, preview_mode): if preview_mode == "index": return {"products": [self]*20} return {"product": self}
from wagtail.models import PreviewableMixin @register_snippet class Product(PreviewableMixin, models.Model): preview_modes = [ ("index", "Index"), ("detail", "Detail"), ] def get_preview_template(self, request, preview_mode): return f"products/{preview_mode}.html" def get_preview_context(self, request, preview_mode): if preview_mode == "index": return {"products": [self]*20} return {"product": self}
How we built it:
Add versioning to your content type by extending the RevisionMixin
class.
from wagtail.models import RevisionMixin ... class Product(RevisionMixin, PreviewableMixin, models.Model): ...
from wagtail.models import RevisionMixin ... class Product(RevisionMixin, PreviewableMixin, models.Model): ...
You can compare revisions and revert to a previous version.
Use DraftStateMixin
to have the ability to save unpublished changes (drafts).
from wagtail.models import DraftStateMixin ... class Product(DraftStateMixin, RevisionMixin, PreviewableMixin, models.Model): ...
from wagtail.models import DraftStateMixin ... class Product(DraftStateMixin, RevisionMixin, PreviewableMixin, models.Model): ...
Unpublished changes are saved as revisions and will not be reflected to the live content until you publish them.
Drafts can be scheduled to be published at a later time.
You can prevent multiple users from editing the same content at the same time by using the LockableMixin
class.
from wagtail.models import LockableMixin ... class Product( DraftStateMixin, LockableMixin, RevisionMixin, PreviewableMixin, models.Model, ): ...
from wagtail.models import LockableMixin ... class Product( DraftStateMixin, LockableMixin, RevisionMixin, PreviewableMixin, models.Model, ): ...
If WorkflowMixin
is enabled, you can define workflows for your content types.
Workflows can be used to moderate content changes before they go live.
from wagtail.models import WorkflowMixin ... class Product( WorkflowMixin, DraftStateMixin, LockableMixin, RevisionMixin, PreviewableMixin, models.Model, ): ...
from wagtail.models import WorkflowMixin ... class Product( WorkflowMixin, DraftStateMixin, LockableMixin, RevisionMixin, PreviewableMixin, models.Model, ): ...
A workflow is a series of tasks (stages) that a piece of content must go through before a final goal (e.g. publishing) is reached.
Developers can write custom "task types" to define the conditions and actions for each task.
class GroupApprovalTask(Task): groups = models.ManyToManyField(Group, ...) def get_actions(self, obj, user): ... def on_action(task_state, user, action_name, **kwargs): ... ...
class GroupApprovalTask(Task): groups = models.ManyToManyField(Group, ...) def get_actions(self, obj, user): ... def on_action(task_state, user, action_name, **kwargs): ... ...
An accessibility checker is built-in to the page editor, powered by axe-core
.
It checks the content for common accessibility issues and suggests improvements.
An optional package, wagtail-ai
, provides AI-powered features in the CMS.
Learn more at wagtail.org
Slides available at slides.laymonage.com/fosdem-2024
Wagtail AI webinar on February 7th: wagtail.org/wagtail-ai
Reach out to [email protected]