#django #python #wagtail

Many sites have settings which are used all over the place. Instead of hardcoding them in the settings file, I use a different approach and make them configurable in the Wagtail admin. Wagtail provides the Site Settings option to do this.

First, start with defining the settings in your model class. Since they are site-wide in my case, I tend to put them in a separate app called base:

base/models.py

 1from django.db import models
 2from wagtail.admin.edit_handlers import TabbedInterface, ObjectList, MultiFieldPanel, FieldPanel, ImageChooserPanel
 3from wagtail.contrib.settings.models import BaseSetting, register_setting
 4
 5@register_setting(icon='mail')
 6class SocialMedia(BaseSetting):
 7
 8    twitter = models.CharField(max_length=255)
 9    twitter.verbose_name = 'Username'
10
11    twitter_sitename = models.CharField(max_length=255, blank=True)
12    twitter_sitename.verbose_name = 'Site Name'
13
14    twitter_description = models.TextField(blank=True)
15    twitter_description.verbose_name = 'Site Description'
16
17    twitter_image = models.ForeignKey(
18        'wagtailimages.Image',
19        null=True,
20        blank=True,
21        default=None,
22        on_delete=models.SET_NULL,
23        related_name='+'
24    )
25    twitter_image.verbose_name = 'Site Images'
26
27    facebook = models.URLField()
28    facebook.verbose_name = 'URL'
29
30    email = models.EmailField()
31
32    github = models.CharField(max_length=255)
33    github.verbose_name = 'Username'
34
35    instagram = models.CharField(max_length=255)
36    instagram.verbose_name = 'Username'
37
38    linkedin = models.CharField(max_length=255)
39    linkedin.verbose_name = 'Username'
40
41    content_panels = [
42        MultiFieldPanel(
43            [
44                FieldPanel('twitter'),
45                FieldPanel('twitter_sitename'),
46                FieldPanel('twitter_description'),
47                ImageChooserPanel('twitter_image'),
48            ],
49            heading="Twitter",
50        ),
51        MultiFieldPanel(
52            [FieldPanel('email')],
53            heading="Email",
54        ),
55        MultiFieldPanel(
56            [FieldPanel('facebook')],
57            heading="Facebook",
58        ),
59        MultiFieldPanel(
60            [FieldPanel('github')],
61            heading="GitHub",
62        ),
63        MultiFieldPanel(
64            [FieldPanel('instagram')],
65            heading="Instagram",
66        ),
67        MultiFieldPanel(
68            [FieldPanel('linkedin')],
69            heading="LinkedIn",
70        ),
71    ]
72
73    edit_handler = TabbedInterface([
74        ObjectList(content_panels, heading="Social Media"),
75    ])

This creates a new setting option in the settings menu which the admin can use to configure the different social media settings. It extends from BaseSetting which provides the core editing capabilities. I also configured a couple of content panels to group the information together.

If you go into the settings, you'll get a form like this:

Defining the settings is one thing, you need to be able to use them as well. The first thing you need to do is to register the settings context processor:

mysite/settings/base.py

 1TEMPLATES = [
 2    {
 3        ...
 4        'OPTIONS': {
 5            'context_processors': [
 6                ...
 7                'wagtail.contrib.settings.context_processors.settings',
 8            ]
 9        }
10    }
11]

You can use them in your templates through {{ settings }}:

1{% load wagtailimages_tags %}
2
3{% image settings.base.SocialMedia.twitter_image max-512x512 as twitter_image %}
4<meta name="twitter:card" content="summary">
5<meta name="twitter:title" content="{{ settings.base.SocialMedia.twitter_sitename }}">
6<meta name="twitter:description" content="{{ settings.base.SocialMedia.twitter_description }}">
7<meta name="twitter:site" content="@{{ settings.base.SocialMedia.twitter }}">
8<meta name="twitter:image" content="{{ self.get_site.root_url }}{{ twitter_image.url }}" />

Note that the app name is used to reference them. Since I defined them in the app called base, I refer to them as:

1{{ settings.base.SocialMedia.twitter_sitename }}

If they would have been defined in an app called blog, you would need to refer to them as follows:

1{{ settings.blog.SocialMedia.twitter_sitename }}

You can also use them from within your Python code:

1def view(request):
2    social_media = SocialMedia.for_request(request)
3    ...