Getting Started With Wagtail CMS

Setting up Wagtail CMS: The cleanest and friendliest CMS available for Django

Getting Started With Wagtail CMS

    Last time we spoke of Django, we installed Django CMS: perhaps the most popular out-of-the-box CMS available for Django. Now that we've explored the most popular CMS, let's move on to the best CMS.

    I realize I'm speaking of something subjective as though it were fact. As much as I'd like to agree that this is wrong, doing so would make for a boring read. I'll stick to my guns here.

    Installing Wagtail

    To makes things easy, we'll start with the obligatory server updates and Python3 setup. This way, the copy-and-paste crew will have no problem following along. It's important to note that this example will take place on a Ubuntu server.

    Let's get started by updating and upgrading our packages:

    $ apt update
    $ apt upgrade -y
    

    Next, we'll just make sure that we have the latest versi0ns of Python3, Python3-dev, and pip3 installed:

    $ sudo apt install python3-pip python3-dev
    $ sudo -H pip3 install --upgrade pip
    

    Finally, we'll follow best practices here by keeping our packages confirmed to a virtual environment we create:

    $ python3 -m venv mysite/env
    $ source mysite/env/bin/activate
    

    Great. With the environment activated, we can start getting to work. Before installing Wagtail, we need to install two dependencies: libjpeg and zlib. These are libraries critical to image compression and the sort:

    $ pip3 install libjpeg zlib
    

    Now we can install Wagtail:

    $ pip3 install wagtail
    

    Good job. For our next trick, we'll create our actual Wagtail project. This installation contains Django at its core: simply installing Wagtail with install Django and create the necessary project structure. I'll be creating the project in the /var/www directory, but feel free to do as you please.

    $ mkdir /var/wwww
    $ cd /var/www
    $ wagtail start mysite
    $ cd mysite
    

    wagtail start mysite is a command that will create a project called mysite in the current directory. Again, name your project as you see fit. With that directory created, change directories so you're inside said project, and install Wagtail dependencies from the requirements.txt file:

    $ pip3 install -r requirements.txt
    

    As with a normal Django project, we need to run our migrations to get things set up.

    $ python3 manage.py migrate
    

    This command will result in the following output:

    Operations to perform:
      Apply all migrations: admin, auth, contenttypes, home, sessions, taggit, wagtailadmin, wagtailcore, wagtaildocs, wagtailembeds, wagtailforms, wagtailimages, wagtailredirects, wagtailsearch, wagtailusers
    Running migrations:
      Applying contenttypes.0001_initial... OK
      Applying auth.0001_initial... OK
      Applying admin.0001_initial... OK
      Applying admin.0002_logentry_remove_auto_add... OK
      Applying admin.0003_logentry_add_action_flag_choices... OK
      Applying contenttypes.0002_remove_content_type_name... OK
      Applying auth.0002_alter_permission_name_max_length... OK
      Applying auth.0003_alter_user_email_max_length... OK
      Applying auth.0004_alter_user_username_opts... OK
      Applying auth.0005_alter_user_last_login_null... OK
      Applying auth.0006_require_contenttypes_0002... OK
      Applying auth.0007_alter_validators_add_error_messages... OK
      ...
    

    Next, we need to created a superuser:

    $ python3 manage.py createsuperuser
    

    Running this will prompt a few details as follows:

    Username (leave blank to use 'root'): myuser
    Email address: [email protected]
    Password:
    Password (again):
    Superuser created successfully.
    

    You're a hero. Now, you're probably anxious to see what all the fuss is about. Let's get your development server up and running so you can start playing around in the browser:

    $ python manage.py runserver
    

    That command should give you the following:

    Performing system checks...
    
    System check identified no issues (0 silenced).
    April 20, 2019 - 03:48:58
    Django version 2.1.8, using settings 'mysite.settings.dev'
    Starting development server at http://127.0.0.1:8000/
    Quit the server with CONTROL-C.
    

    Great work. Now if you visit your server's default address on port 8000, you should be able to hit the barebones Wagtail project you just created. To reach the admin, hit http://localhost:8000/admin/.

    Config files

    As mentioned, installing Wagtail will immediately set up a Django project. If that's so, where is our famous settings.py file? It's actually split into three files: base.py, dev.py, and production.py. All these are stored here:

    $ cd mysite/settings
    

    base.py

    base.py is the closest to Django's settings.py file. This contains all settings which are common to both our dev and prod environments.

    """
    Django settings for mysite project.
    
    Generated by 'django-admin startproject' using Django 2.1.8.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/2.1/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/2.1/ref/settings/
    """
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    import os
    
    PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    BASE_DIR = os.path.dirname(PROJECT_DIR)
    
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'home',
        'search',
    
        'wagtail.contrib.forms',
        'wagtail.contrib.redirects',
        'wagtail.embeds',
        'wagtail.sites',
        'wagtail.users',
        'wagtail.snippets',
        'wagtail.documents',
        'wagtail.images',
        'wagtail.search',
        'wagtail.admin',
        'wagtail.core',
    
        'modelcluster',
        'taggit',
    
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
    ]
    
    MIDDLEWARE = [
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'django.middleware.security.SecurityMiddleware',
    
        'wagtail.core.middleware.SiteMiddleware',
        'wagtail.contrib.redirects.middleware.RedirectMiddleware',
    ]
    
    ROOT_URLCONF = 'mysite.urls'
    
    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',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'mysite.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
    
    
    # Password validation
    # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/2.1/topics/i18n/
    
    LANGUAGE_CODE = 'en-us'
    
    TIME_ZONE = 'UTC'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/2.1/howto/static-files/
    
    STATICFILES_FINDERS = [
        'django.contrib.staticfiles.finders.FileSystemFinder',
        'django.contrib.staticfiles.finders.AppDirectoriesFinder',
    ]
    
    STATICFILES_DIRS = [
        os.path.join(PROJECT_DIR, 'static'),
    ]
    
    # ManifestStaticFilesStorage is recommended in production, to prevent outdated
    # Javascript / CSS assets being served from cache (e.g. after a Wagtail upgrade).
    # See https://docs.djangoproject.com/en/2.1/ref/contrib/staticfiles/#manifeststaticfilesstorage
    STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
    
    STATIC_ROOT = os.path.join(BASE_DIR, 'static')
    STATIC_URL = '/static/'
    
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    MEDIA_URL = '/media/'
    
    
    # Wagtail settings
    
    WAGTAIL_SITE_NAME = "mysite"
    
    # Base URL to use when referring to full URLs within the Wagtail admin backend -
    # e.g. in notification emails. Don't include '/admin' or a trailing slash
    BASE_URL = 'http://example.com'
    

    dev.py

    As you might imagine, settings stored in either dev.py or production.py are specific to dev or production, respectively. dev.py comes with some presets, such as setting DEBUG to True, as well as a pre-populated SECRET_KEY:

    from .base import *
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = 'i!9r654&2us(2g!2ep#n*vufp-)z#mn+^qid_w8o^thnsj)nbm'
    
    # SECURITY WARNING: define the correct hosts in production!
    ALLOWED_HOSTS = ['*']
    
    EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
    
    
    try:
        from .local import *
    except ImportError:
        pass
    

    production.py

    Unlike dev.py, production.py comes with no such predetermined values. The understanding is that you must set these values yourself: after all, if anybody were to grab a hold of the secret kept in production.py, your career as a developer is basically over:

    from .base import *
    
    DEBUG = False
    
    try:
        from .local import *
    except ImportError:
        pass
    
    Todd Birchard's' avatar
    New York City Website
    Product manager turned engineer with an ongoing identity crisis. Breaks everything before learning best practices. Completely normal and emotionally stable.

    Product manager turned engineer with an ongoing identity crisis. Breaks everything before learning best practices. Completely normal and emotionally stable.