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)