DjangoDjango

How to Hide And Auto-populate Title Field of a Page in Wagtail CMS

Feb 20, 2018 · Updated: Jul 17, 2021 · by Tim Kamanin

Given

from django.db import models
from wagtail.wagtailsnippets.models import register_snippet


class CountryPage(Page):
    country = models.ForeignKey(
        "wagtail_simple_countries.Country",
        blank=False,
        null=True,
        on_delete=models.SET_NULL,
    )


class Country(models.Model):
    name = models.CharField(max_length=128)

Task

I want to have the title field of my CountryPage to be auto-populated with a name of the related country model.

Solution

  1. Add content_panels to CountryPage without merging them with content_panels of the parent Page class.

    class CountryPage(Page):
        country = models.ForeignKey(
            "wagtail_simple_countries.Country",
            blank=False,
            null=True,
            on_delete=models.SET_NULL,
        )
    
        content_panels = [
            FieldPanel("country"),
        ]
    

    We do this to exclude title field from the edit page form.

  2. Override save method of the CountryPage model to set title from the related Country name.

    class CountryPage(Page):
        country = models.ForeignKey(
            "wagtail_simple_countries.Country",
            blank=False,
            null=True,
            on_delete=models.SET_NULL,
        )
    
        content_panels = [
            FieldPanel("country"),
        ]
    
        def save(self, *args, **kwargs):
            self.title = self.country.name
            super().save(*args, **kwargs)
    

  3. Add a Javascript code that auto-populates slug field with a value of the chosen country:

    function initSlugAutoPopulateFromCountry() {
      var slugFollowsTitle = false;
    
      $("#id_country").on("focus", function () {
        /* slug should only follow the title field if its value matched the title's value at the time of focus */
        var currentSlug = $("#id_slug").val();
        // If select value is not empty we get current selection's label.
        var value =
          this.value !== "" ? this.options[this.selectedIndex].innerHTML : "";
        var slugifiedTitle = cleanForSlug(value, true);
        slugFollowsTitle = currentSlug == slugifiedTitle;
      });
    
      $("#id_country").on("change", function () {
        if (slugFollowsTitle) {
          var label = this.options[this.selectedIndex].innerHTML;
          var slugifiedTitle = cleanForSlug(label, true);
          $("#id_slug").val(slugifiedTitle);
        }
      });
    }
    
    $(function () {
      /* Only non-live pages should auto-populate the slug from the title */
      if (!$("body").hasClass("page-is-live")) {
        initSlugAutoPopulateFromCountry();
      }
    });
    

    You can save this files as static/js/slug_from_country.js

  4. Load the Javascript code into Wagtail's page editor via hooks. To do that, create a wagtail_hooks.py file in your Django app with the following content:

    from django.utils.html import format_html_join
    from django.conf import settings
    from wagtail.wagtailcore import hooks
    
    
    @hooks.register("insert_editor_js")
    def editor_js():
        js_files = [
            "countries/js/slug_from_country.js",
        ]
        js_includes = format_html_join(
            "\n",
            '<script src="{0}{1}"></script>',
            ((settings.STATIC_URL, filename) for filename in js_files),
        )
        return js_includes
    

That's all; you should now see a Country Page edit form without the title field but with the country name selector that dynamically populates the slug field. After you save the page, its title should also be set to the related country name.

Hey, if you've found this useful, please share the post to help other folks find it:

There's even more:

Subscribe for updates

  • via Twitter: @timonweb
  • old school RSS:
  • or evergreen email ↓ ↓ ↓