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

20 Feb · by Tim Kamanin · 4 min read

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.

Comments