0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-11-29 09:33:54 +01:00
wagtail/docs/tutorial/create-footer_for_all_pages.md
LB Johnston b41e196489 Formatting documentation for whitespace fixes
Also include actual footnote reference for upgrading patch release note
2024-01-21 19:59:57 +10:00

14 KiB
Raw Blame History

Create a footer for all pages

Weve just released this new tutorial. Do you want to provide feedback? Please complete our [2023 tutorial feedback form](https://wagtail.org/gsod-2023-feedback/).

The next step is to create a footer for all pages of your portfolio site. You can display social media links and other information in your footer.

Add a base app

Now, create a general-purpose app named base. To generate the base app, run the command:

python manage.py startapp base

After generating the base app, you must install it on your site. In your mysite/settings/base.py file, add "base" to the INSTALLED_APPS list.

Create navigation settings

Now, go to your base/models.py file and add the following lines of code:

from django.db import models
from modelcluster.models import ClusterableModel
from wagtail.admin.panels import (
    FieldPanel,
    MultiFieldPanel,
)
from wagtail.contrib.settings.models import (
    BaseGenericSetting,
    register_setting,
)

@register_setting
class NavigationSettings(BaseGenericSetting):
    twitter_url = models.URLField(verbose_name="Twitter URL", blank=True)
    github_url = models.URLField(verbose_name="GitHub URL", blank=True)
    mastodon_url = models.URLField(verbose_name="Mastodon URL", blank=True)

    panels = [
        MultiFieldPanel(
            [
                FieldPanel("twitter_url"),
                FieldPanel("github_url"),
                FieldPanel("mastodon_url"),
            ],
            "Social settings",
        )
    ]

In the preceding code, the register_setting decorator registers your NavigationSettings models. You used the BaseGenericSetting base model class to define a settings model that applies to all web pages rather than just one page.

Now, migrate your database by running the commands python manage.py makemigrations and python manage.py migrate. After migrating your database, reload your admin interface. You'll get the error 'wagtailsettings' is not a registered namespace. This is because you haven't install the wagtail.contrib.settings module.

The wagtail.contrib.settings module defines models that hold common settings across all your web pages. So, to successfully import the BaseGenericSetting and register_setting, you must install the wagtail.contrib.settings module on your site. To install wagtail.contrib.settings, go to your mysite/settings/base.py file and add "wagtail.contrib.settings" to the INSTALLED_APPS list.

Also, you have to register the settings context processor. Registering settings context processor makes site-wide settings accessible in your templates. To register the settings context processor, modify your mysite/settings/base.py file as follows:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [
            os.path.join(PROJECT_DIR, "templates"),
        ],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",

                # Add this to register the _settings_ context processor:
                "wagtail.contrib.settings.context_processors.settings",
            ],
        },
    },
]

(add_your_social_media_links)=

To add your social media links, reload your admin interface and click Settings from your Sidebar. You can see your Navigation Settings. Clicking the Navigation Settings gives you a form to add your social media account links.

You must provide a template to display the social media links you added through the admin interface.

Create an includes folder in your mysite/templates folder. Then in your newly created mysite/templates/includes folder, create a footer.html file and add the following to it:

<footer>
    <p>Built with Wagtail</p>

    {% with twitter_url=settings.base.NavigationSettings.twitter_url github_url=settings.base.NavigationSettings.github_url mastodon_url=settings.base.NavigationSettings.mastodon_url %}
        {% if twitter_url or github_url or mastodon_url %}
            <p>
                Follow me on:
                {% if github_url %}
                    <a href="{{ github_url }}">GitHub</a>
                {% endif %}
                {% if twitter_url %}
                    <a href="{{ twitter_url }}">Twitter</a>
                {% endif %}
                {% if mastodon_url %}
                    <a href="{{ mastodon_url }}">Mastodon</a>
                {% endif %}
            </p>
        {% endif %}
    {% endwith %}
</footer>

Now, go to your mysite/templates/base.html file and modify it as follows:

<body class="{% block body_class %}{% endblock %}">
    {% wagtailuserbar %}

    {% block content %}{% endblock %}

    {# Add this to the file: #}
    {% include "includes/footer.html" %}

    {# Global javascript #}
    <script type="text/javascript" src="{% static 'js/mysite.js' %}"></script>

    {% block extra_js %}
    {# Override this in templates to add extra javascript #}
    {% endblock %}
</body>

Now, reload your homepage. You'll see your social media links at the bottom of your homepage.

Create editable footer text with Wagtail Snippets

Having only your social media links in your portfolio footer isn't ideal. You can add other items, like site credits and copyright notices, to your footer. One way to do this is to use the Wagtail snippet feature to create an editable footer text in your admin interface and display it in your site's footer.

To add a footer text snippet to your admin interface, modify your base/model.py file as follows:

from django.db import models
from wagtail.admin.panels import (
    FieldPanel,
    MultiFieldPanel,

    # import PublishingPanel:
    PublishingPanel,
)

# import RichTextField:
from wagtail.fields import RichTextField

# import DraftStateMixin, PreviewableMixin, RevisionMixin, TranslatableMixin:
from wagtail.models import (
    DraftStateMixin,
    PreviewableMixin,
    RevisionMixin,
    TranslatableMixin,
)

from wagtail.contrib.settings.models import (
    BaseGenericSetting,
    register_setting,
)

# import register_snippet:
from wagtail.snippets.models import register_snippet

# ...keep the definition of the NavigationSettings model and add the FooterText model:
@register_snippet
class FooterText(
    DraftStateMixin,
    RevisionMixin,
    PreviewableMixin,
    TranslatableMixin,
    models.Model,
):

    body = RichTextField()

    panels = [
        FieldPanel("body"),
        PublishingPanel(),
    ]

    def __str__(self):
        return "Footer text"

    def get_preview_template(self, request, mode_name):
        return "base.html"

    def get_preview_context(self, request, mode_name):
        return {"footer_text": self.body}

    class Meta(TranslatableMixin.Meta):
        verbose_name_plural = "Footer Text"

In the preceding code, the FooterText class inherits from several Mixins, the DraftStateMixin, RevisionMixin, PreviewableMixin, and TranslatableMixin. In Django, Mixins are reusable pieces of code that define additional functionality. They are implemented as Python classes, so you can inherit their methods and properties.

Since your FooterText model is a Wagtail snippet, you must manually add Mixins to your model. This is because snippets aren't Wagtail Pages in their own right. Wagtail Pages don't require Mixins because they already have them.

DraftStateMixin is an abstract model that you can add to any non-page Django model. You can use it for drafts or unpublished changes. The DraftStateMixin requires RevisionMixin.

RevisionMixin is an abstract model that you can add to any non-page Django model to save revisions of its instances. Every time you edit a page, Wagtail creates a new Revision and saves it in your database. You can use Revision to find the history of all the changes that you make. Revision also provides a place to keep new changes before they go live.

PreviewableMixin is a Mixin class that you can add to any non-page Django model to preview any changes made.

TranslatableMixin is an abstract model you can add to any non-page Django model to make it translatable.

Also, with Wagtail, you can set publishing schedules for changes you made to a Snippet. You can use the PublishingPanel() method to schedule revisions in your FooterText.

The __str__ method defines a human-readable string representation of an instance of the FooterText class. It returns the string "Footer text".

The get_preview_template method determines the template for rendering the preview. It returns the template name "base.html".

The get_preview_context method defines the context data that you can use to render the preview template. It returns a key "footer_text" with the content of the body field as its value.

The Meta class holds metadata about the model. It inherits from the TranslatableMixin.Meta class and sets the verbose_name_plural attribute to "Footer Text".

Now, migrate your database by running python manage.py makemigrations and python manage.py migrate. After migrating, restart your server and then reload your admin interface. You can now find Snippets in your Sidebar.

(add_footer_text)=

To add your footer text, go to your admin interface. Click Snippets in your Sidebar and add your footer text.

In this tutorial, you'll use a custom template tag to display your footer text.

In your base folder, create a templatetags folder. Within your new templatetags folder, create the following files:

  • __init__.py
  • navigation_tags.py

Leave your base/templatetags/__init__.py file blank and add the following to your base/templatetags/navigation_tags.py file:

from django import template

from base.models import FooterText

register = template.Library()


@register.inclusion_tag("base/includes/footer_text.html", takes_context=True)
def get_footer_text(context):
    footer_text = context.get("footer_text", "")

    if not footer_text:
        instance = FooterText.objects.filter(live=True).first()
        footer_text = instance.body if instance else ""

    return {
        "footer_text": footer_text,
    }

In the preceding code, you imported the template module. You can use it to create and render template tags and filters. Also, you imported the FooterText model from your base/models.py file.

register = template.Library() creates an instance of the Library class from the template module. You can use this instance to register custom template tags and filters.

@register.inclusion_tag("base/includes/footer_text.html", takes_context=True) is a decorator that registers an inclusion tag named get_footer_text. "base/includes/footer_text.html" is the template path that you'll use to render the inclusion tag. takes_context=True indicates that the context of your footer_text.html template will be passed as an argument to your inclusion tag function.

The get_footer_text inclusion tag function takes a single argument named context. context represents the template context where you'll use the tag.

footer_text = context.get("footer_text", "") tries to retrieve a value from the context using the key footer_text. The footer_text variable stores any retrieved value. If there is no footer_text value within the context, then the variable stores an empty string "".

The if statement in the get_footer_text inclusion tag function checks whether the footer_text exists within the context. If it doesn't, the if statement proceeds to retrieve the first published instance of the FooterText from the database. If a published instance is found, the statement extracts the body content from it. However, if there's no published instance available, it defaults to an empty string.

Finally, the function returns a dictionary containing the "footer_text" key with the value of the retrieved footer_text content. You'll use this dictionary as context data when rendering your footer_text template.

To use the returned dictionary, create a templates/base/includes folder in your base folder. Then create a footer_text.html file in your base/templates/base/includes/ folder and add the following to it:

{% load wagtailcore_tags %}

<div>
    {{ footer_text|richtext }}
</div>

Add your footer_text template to your footer by modifying your mysite/templates/includes/footer.html file:

{# Load navigation_tags at the top of the file: #}
{% load navigation_tags %}

<footer>
    <p>Built with Wagtail</p>

    {% with twitter_url=settings.base.NavigationSettings.twitter_url github_url=settings.base.NavigationSettings.github_url mastodon_url=settings.base.NavigationSettings.mastodon_url %}
        {% if twitter_url or github_url or mastodon_url %}
            <p>
                Follow me on:
                {% if github_url %}
                    <a href="{{ github_url }}">GitHub</a>
                {% endif %}
                {% if twitter_url %}
                    <a href="{{ twitter_url }}">Twitter</a>
                {% endif %}
                {% if mastodon_url %}
                    <a href="{{ mastodon_url }}">Mastodon</a>
                {% endif %}
            </p>
        {% endif %}
    {% endwith %}

    {# Add footer_text: #}
    {% get_footer_text %}
</footer>

Now, restart your server and reload your homepage. For more information on how to render your Wagtail snippets, read Rendering snippets.

Well done! 👏 You now have a footer across all pages of your portfolio site. In the next section of this tutorial, you'll learn how to set up a site menu for linking to your homepage and other pages as you add them.