Wagtail pages¶
Wagtail pages are
Django models
that are constructed of
fields, StreamFields, and panels
that are rendered in a standard way.
All CFPB Wagtail pages should inherit from the
v1.models.base.CFGOVPage
class.
Note
Before creating a new Wagtail page type please consider whether one of our existing page types can meet your needs. Talk to the consumerfinance.gov product owners if your content is significantly different from anything else on the site or a specific maintenance efficiency will be gained from a new page type.
There are types of information defined on a new Wagtail page model: basic database fields (like any Django model), specialized database fields called StreamFields that allow for freeform page content, and editor panels that present these fields to content editors.
Fields¶
Database fields in Wagtail pages work exactly the same as in Django models, and Wagtail pages can use any Django model field.
For example, our BrowsePage
includes a standard Django BooleanField
that allows content editors to toggle secondary navigation sibling pages:
from django.db import models
from v1.models.base import CFGOVPage
class BrowsePage(CFGOVPage):
secondary_nav_exclude_sibling_pages = models.BooleanField(default=False)
StreamFields¶
StreamFields are special Django model fields provided by Wagtail for freeform page content. They allow a content editor to pick any number number of optional components and place them in any order within their StreamField. In practice, this provides the flexibility of a large rich text field, with the structure of individual components.
For example, our LandingPage
page model
includes a header
StreamField
that can have a hero and/or a text introduction:
from wagtail.fields import StreamField
from v1.atomic_elements import molecules
from v1.models import CFGOVPage
class LandingPage(CFGOVPage):
header = StreamField([
('hero', molecules.Hero()),
('text_introduction', molecules.TextIntroduction()),
], blank=True)
The specifics of StreamField block components can be found in Creating and Editing Wagtail Components.
Panels¶
Editor panels define how the page's fields and StreamFields will be organized for content editors; they correspond to the tabs that appear across the top of the edit view for a page in the Wagtail admin.
The base Wagtail Page
class and the CFGOVPage
subclass of it
define specific sets of panels to which all fields should be added:
content_panels
: For page body content. These fields appear on the "General Content" tab when editing a page.sidefoot_panels
: For page sidebar or footer content. These fields appear on the "Sidebar" tab when editing a page.settings_panels
: Page configuration such as the categories, tags, scheduled publishing, etc. Appears on the "Configuration" tab when editing a page.
Most fields will simply require a FieldPanel
to be added to one of the sets of panels above.
StreamFields will require a StreamFieldPanel
.
See the Wagtail documentation for additional, more complex panel options.
For example, in our BrowsePage
(used in the database fields example above),
the secondary_nav_exclude_sibling_pages
BooleanField
is added to the sidefoot_panels
as a FieldPanel
:
from django.db import models
from wagtail.admin.edit_handlers import FieldPanel
from v1.models.base import CFGOVPage
class BrowsePage(CFGOVPage):
secondary_nav_exclude_sibling_pages = models.BooleanField(default=False)
# …
sidefoot_panels = CFGOVPage.sidefoot_panels + [
FieldPanel('secondary_nav_exclude_sibling_pages'),
]
Because secondary_nav_exclude_sibling_pages
is a boolean field,
this creates a checkbox on the "Sidebar/Footer" tab when editing a page.
Checking or unchecking that checkbox will set the value of secondary_nav_exclude_sibling_pages
when the page is saved.
In our LandingPage
(used in the StreamFields example above),
the header
StreamField
is added to the content_panels
as a StreamFieldPanel
:
from wagtail.admin.edit_handlers import StreamFieldPanel
from wagtail.fields import StreamField
from v1.atomic_elements import molecules
from v1.models import CFGOVPage
class LandingPage(CFGOVPage):
header = StreamField([
('hero', molecules.Hero()),
('text_introduction', molecules.TextIntroduction()),
], blank=True)
# …
content_panels = CFGOVPage.content_panels + [
StreamFieldPanel('header'),
# …
]
Parent / child page relationships¶
Wagtail provides two attributes to page models that enable restricting the types of subpages or parent pages a particular page model can have. On any page model:
parent_page_types
limits which page types this type can be created under.subpage_types
limits which page types can be created under this type.
For example, in our interactive regulations page models
we have a RegulationLandingPage
that can be created anywhere in the page tree.
RegulationLandingPage
, however, can only have two types of pages created within it:
RegulationPage
and RegulationSearchPage
.
This parent/child relationship is expressed by setting subpage_types
on RegulationLandingPage
and parent_page_types
on RegulationPage
and RegulationSearchPage
to a model name in the form 'app_label.ModelName':
from v1.models import CFGOVPage
class RegulationLandingPage(CFGOVPage):
subpage_types = ['regulations3k.RegulationPage', 'regulations3k.RegulationsSearchPage']
class RegulationsSearchPage(CFGOVPage):
parent_page_types = ['regulations3k.RegulationLandingPage']
subpage_types = []
class RegulationPage(CFGOVPage):
parent_page_types = ['regulations3k.RegulationLandingPage']
subpage_types = []
Note
We prevent child pages from being added to RegulationPage
and RegulationSearchPage
by setting subpage_types
to an empty list.
Template rendering¶
New Wagtail page types will usually need to make customizations to their base template
when rendering the page.
This is done by overriding the template
attribute on the page model.
For example, the interactive regulations landing page
includes a customized list of recently issued notices that gets loaded dynamically from the Federal Register.
To do this it provides its own template that inherits from our base templates
and overrides the content__sidebar
block to include a separate recent_notices
template:
from v1.models import CFGOVPage
class RegulationLandingPage(CFGOVPage):
template = 'regulations3k/landing-page.html'
And in regulations3k/landing-page.html
:
{% extends 'layout-2-1.html' %}
{% import 'recent-notices.html' as recent_notices with context %}
{% block content_sidebar scoped -%}
{{ recent_notices }}
{%- endblock %}