This article explains the steps I had taken to set up Wagtail along with PostgreSQL using containerization techniques.
As part of a commitment to a friend in the middle of the year, and to enhance my Python skills, ventured into learning, understanding, and customizing Wagtail CMS. I have had bits of Python experience and Docker during recent years and also have some knowledge on WordPress websites. Learning and improving my skills on Wagtail was a wonderful experience during my spare time over the last few months.
In this article, I'm sharing (or rather journaling) some of the things that I feel could be useful for others. Feel free to share your comments and/or feedback on how it can be improved.
Wagtail - is a leading open-source Content Management System built using Python. Read more here: [https://wagtail.org]
Why Wagtail on Docker?
As you might be aware, standard Python practice is to use virtualenvs to isolate different development environments, as I was trying out different stuff on my PC it was getting a bit annoying to switch in and out of virtualenvs and often spend time and realizing using the wrong environments.
Why this blog/article?
As I started, I did come across quite a few blog/websites explaining the process/steps, however I felt they required Python and/or Wagtail be installed on their PC or where addressing use cases that were not simple. As part of learning Docker as well, I decided to embark to see if this can be done; I succeeded with that and sharing here in case if anyone might need it.
Let's get to it
Here is the TOC
- Dockerfile
- Changing the Database
- compose.yaml
- VS Code extensions
Preparing the Dockerfile
- As my aim was to get Wagtail fully self-contained, the first step was to create a Docker image that would have Python and other libraries required for Wagtail/Django installed. This was fairly straightforward.
- Below is the basic Dockerfile
## Install system dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
## Install the dependencies and create the image with Wagtail!
RUN pip install --no-cache-dir wagtail==6.2.3 psycopg2>=2.9,<3
Changing database from SQLite to PostgreSQL
- While the above worked perfectly using SQLite as the database, I wanted to use PostgreSQL!
- Database parameters are defined under settings/base.py within the Wagtail site.
- I Initially started writing a bash script to modify the settings/base.py, but it later struck me that I could easily do this with a simple Python script.
- This script will be executed by the entry point scripts when the composer launches the services. Below is the Python function that I used to modify the database parameters with settings/base.py.
def replace_sqlite_with_postgres(settings_file_path):
# Define the new PostgreSQL DATABASES configuration
new_databases_config = """
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('POSTGRES_DB', 'mydatabase'),
'USER': os.getenv('POSTGRES_USER', 'myuser'),
'PASSWORD': os.getenv('POSTGRES_PASSWORD', 'mypassword'),
'HOST': os.getenv('POSTGRES_HOST', 'localhost'),
'PORT': os.getenv('POSTGRES_PORT', '5432'),
}
}
"""
# Read in the original base.py file
with open(settings_file_path, "r") as file:
content = file.read()
# Use regex to find and replace the DATABASES block
content = re.sub(
r"DATABASES\s*=\s*\{.*?\n\}", # Match DATABASES block
new_databases_config.strip(), # Replace with the new config
content,
flags=re.DOTALL
)
# Write the modified content back to base.py
with open (settings_file_path, "w") as file:
file.write(content)
- Observe closely, I've used environment variables to get the Database configuration params, which made it easier to share between the services
- with the Python script defined, had to modify the Dockerfile to copy this as well, below is the complete Dockerfile
FROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
libjpeg62-turbo-dev \
zlib1g-dev \
libwebp-dev \
&& rm -rf /var/lib/apt/lists/*
COPY replace_sqlite.py .
RUN pip install --no-cache-dir wagtail==6.2.3 "psycopg2>=2.9,<3"
EXPOSE 8000
Preparing the compose.yaml
- As I wanted to segregate both the App and DB separately, chose to create two separate services.
- Setting up the database service was straightforward, had to make some changes to handle database configurations for the Web App. Below is the source for compose.yaml
services:
web:
build: .
container_name: wagtail_web
volumes:
- .:/app
ports:
- "58000:8000"
depends_on:
- db
env_file:
- .env
entrypoint: ["/bin/bash", "/app/entrypoint.sh"]
db:
image: postgres:13
container_name: wagtail_db
env_file:
- .env
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "55432:${POSTGRES_PORT}"
volumes:
postgres_data:
- entrypoint directive was very handy to achieve what I intended to do. - Wrote a small bash script that repeats most of the commands required to start a new Wagtail website, the entrypoint.sh Bash script provided below was called using the entrypoint directive.
#!/bin/bash
# Check if the Wagtail project directory exists
if [ ! -d "/app/$SITE_NAME" ]; then
wagtail start $SITE_NAME
cd /app/$SITE_NAME
pip3 install -r requirements.txt
python ../replace_sqlite.py ./$SITE_NAME/settings/base.py
python manage.py migrate
echo "Creating superuser..."
echo "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('$SITE_ADMIN_USER', '$SITE_ADMIN_USER', '$SITE_ADMIN_PASSWORD')" | python manage.py shell
fi
cd /app/$SITE_NAME
python manage.py runserver 0.0.0.0:8000
- I also introduced a few commands to automate most of the stuff that you will have to do when starting a new website.
- This script does the following when launched for the first time:
- Creates a fresh Wagtail website using the name provided in .env file
- Installs the necessary requirements for Wagtail
- Calls the replace_sqlite.py script to change to PostgreSQL
- Runs the initial database migrations required for Wagtail
- Creates the super user for the Wagtail website based on the variables defined in .env file
- On subsequent starts, it will just launch the existing Wagtail website.
Working with VS Code
- At this point, I managed to get the default Wagtail site up and running, but open VS Code and face with this issue :( ! Squiggles due to missing Python environment
- The Dev Containers and the Remote Development extensions came in handy to achieve this. Below is the basic devcontainer.json configuration that helped me with this.
{
"name": "Docker Wagtail Web Workspace",
"dockerComposeFile": "../compose.yaml",
"service": "web",
"workspaceFolder": "/app/mysite"
}
- And with this when I open Reopen in Container the Squiggles are gone!
! Working VS Code with the environment from Container
Conclusion
- While I'm still learning and experimenting with Wagtail and Python, I was happy to learn and create a fully self-contained working environment
- This will allow anyone to share their Wagtail project easily without having to give any specific instructions on setting this up.
- Apart from that, with Dev Containers it is also going to be easier to work with any Containers that are deployed remotely too
-
All of the code referenced in this article is available in my repo (Wagtail-Docker) [https://github.com/shariharan1/wagtail_docker], feel free to pull/edit/customize to your needs. Let me know in the comments if any feedback or queries.
References
Top comments (0)