I made a single-page site in static HTML with shiny screen-height divs, and a smooth scrolling function from a navbar. The website is expected to have a mixture of simple body text, a few images, and a card-deck. It all worked nicely and I was happy.
While I have used wagtail for very simple websites in the past, I cannot work out a way of making a single page site where the home page goes at the top followed by all the child pages in order. Is this possible with Wagtail's page models?
I've done something like this a while ago by rendering subpages of my HomePage
class as section on this HomePage
. There was some minor customization at various places involved (see below). Perhaps the hardest part was rewriting the page-urls of the "section pages" to point to the HomePage
and the correct anchor on this HomePage
(see below get_url_parts
).
I've recycled the wagtail page class build-in in_menu
to generate a navbar with/to the relevant sections.
While I've been trying to catch everything, I hope nothing relevant has slipped my mind...
Code
models/pages.py
class HomePage(Page):
"""
We only have one frontend page, on which we render child pages as subsections.
"""
parent_page_types = [
'wagtailcore.Page',
]
# The following page types (here named "sections") are standard wagtail
# page models, but rendered on this HomePage.
subpage_types = [
'TextSection',
'WhereSection',
'ContactSection',
...
]
# fields...
# panels...
def get_subsections(self):
"""
Return page queryset with sections to render on the HomePage.
"""
return (
self
.get_children()
.specific()
.live()
.public()
)
def get_context(self, request):
context = super().get_context(request)
context['subsections'] = self.get_subsections()
context['nav_sections'] = self.get_subsections().in_menu()
return context
models/sections.py
class BaseSection(models.Model):
"""
BaseSection abstract base class. All HomePage sections should inherit
from this class.
"""
parent_page_types = ['HomePage']
subpage_types = []
fields...
panels...
class Meta:
abstract = True
def get_url_parts(self, request=None, *args, **kwargs):
"""
Customising URL patterns for a page model
http://docs.wagtail.io/en/latest/topics/pages.html#customising-url-patterns-for-a-page-model
Rewrite page path to corresponding anchor of this section on the
containing page.
"""
url_parts = super().get_url_parts(request=request)
if url_parts is None:
return None
else:
site_id, root_url, page_path = url_parts
_cust_page_path = '#section-{}'.format(page_path.replace('/', ''))
return (site_id, root_url, _cust_page_path)
class TextSection(BaseSection, Page):
template = 'sections/text_section.html'
body = RichTextField()
content_panels = BaseSection.content_panels + [
FieldPanel('body'),
]
class FooSection(BaseSection, Page):
...
The rest is done via the template layer: looping over all subsection on the homepage:
# templates/home_page.html
{% extends 'base.html' %}
{% block navbar %}
{% include 'includes/navbar.html' %}
{% endblock navbar %}
{% block page_content %}
{% for subsection in subsections.all %}
{% with subsection.specific as subsection %}
{% include subsection.template with subsection=subsection %}
{% endwith %}
{% endfor %}
{% endblock %}
# templates/includes/navbar.html
{% load wagtailroutablepage_tags %}
<nav>
{% for item in nav_sections %}
<a
href="{% if not is_homepage %}{% routablepageurl page 'homepage' %}{% endif %}#section-{{ item.slug }}"
>
{{ item.title }}
</a>
{% endfor %}
</nav>
# templates/sections/section_base.html
<section id="section-{{ subsection.slug }}" class="{{ subsection.content_type|slugify }}">
{{ subsection.title }}
{% block content %}
{% endblock content %}
</section>
# templates/sections/text_section.html
{% extends 'sections/section_base.html' %}
{% block content %}
{{ subsection.body|richtext }}
{% endblock content %}
If you're building one page using Django templates, you should be able to pass a list of pages to your template using the get_context() method.
If it's an SPA, you could use AJAX requests to fetch other pages data from the built-in Wagtail API. If it doesn't fully suit your needs, you could still build your own API with e.g. the Django Rest Framework.