<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hari Shanmugam</title>
    <description>The latest articles on DEV Community by Hari Shanmugam (@hari_shanmugam).</description>
    <link>https://dev.to/hari_shanmugam</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2474864%2Fdb7bd322-5ca4-4bbe-a1ad-c0440ebea586.png</url>
      <title>DEV Community: Hari Shanmugam</title>
      <link>https://dev.to/hari_shanmugam</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hari_shanmugam"/>
    <language>en</language>
    <item>
      <title>Wagtail on Docker</title>
      <dc:creator>Hari Shanmugam</dc:creator>
      <pubDate>Sun, 24 Nov 2024 12:47:14 +0000</pubDate>
      <link>https://dev.to/hari_shanmugam/wagtail-on-docker-2dfb</link>
      <guid>https://dev.to/hari_shanmugam/wagtail-on-docker-2dfb</guid>
      <description>&lt;p&gt;This article explains the steps I had taken to set up Wagtail along with PostgreSQL using containerization techniques. &lt;/p&gt;

&lt;p&gt;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. &lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Wagtail - is a leading open-source Content Management System built using Python. Read more here: [&lt;a href="https://wagtail.org" rel="noopener noreferrer"&gt;https://wagtail.org&lt;/a&gt;]&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Wagtail on Docker?
&lt;/h2&gt;

&lt;p&gt;As you might be aware, standard Python practice is to use &lt;em&gt;virtualenvs&lt;/em&gt; 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 &lt;em&gt;virtualenvs&lt;/em&gt; and often spend time and realizing using the wrong environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this blog/article?
&lt;/h3&gt;

&lt;p&gt;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. &lt;/p&gt;

&lt;h2&gt;
  
  
  Let's get to it
&lt;/h2&gt;

&lt;p&gt;Here is the TOC&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Dockerfile&lt;/li&gt;
&lt;li&gt;Changing the Database&lt;/li&gt;
&lt;li&gt;compose.yaml &lt;/li&gt;
&lt;li&gt;VS Code extensions&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Preparing the &lt;em&gt;Dockerfile&lt;/em&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;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. &lt;/li&gt;
&lt;li&gt;Below is the basic &lt;em&gt;Dockerfile&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Install system dependencies
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \
    build-essential \
    libpq-dev \
    &amp;amp;&amp;amp; 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&amp;gt;=2.9,&amp;lt;3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Changing database from SQLite to PostgreSQL
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;While the above worked perfectly using SQLite as the database, I wanted to use PostgreSQL! &lt;/li&gt;
&lt;li&gt;Database parameters are defined under &lt;em&gt;settings/base.py&lt;/em&gt; within the Wagtail site. &lt;/li&gt;
&lt;li&gt;I Initially started writing a bash script to modify the &lt;em&gt;settings/base.py&lt;/em&gt;, but it later struck me that I could easily do this with a simple Python script. &lt;/li&gt;
&lt;li&gt;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 &lt;em&gt;settings/base.py&lt;/em&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Observe closely, I've used environment variables to get the Database configuration params, which made it easier to share between the services&lt;/li&gt;
&lt;li&gt;with the Python script defined, had to modify the &lt;em&gt;Dockerfile&lt;/em&gt; to copy this as well, below is the complete &lt;em&gt;Dockerfile&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:3.11-slim
WORKDIR /app
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \
    build-essential \
    libpq-dev \
    libjpeg62-turbo-dev \
    zlib1g-dev \
    libwebp-dev \
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*

COPY replace_sqlite.py .

RUN pip install --no-cache-dir wagtail==6.2.3 "psycopg2&amp;gt;=2.9,&amp;lt;3"
EXPOSE 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Preparing the &lt;em&gt;compose.yaml&lt;/em&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;As I wanted to segregate both the App and DB separately, chose to create two separate services.&lt;/li&gt;
&lt;li&gt;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 &lt;em&gt;compose.yaml&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;entrypoint&lt;/em&gt; 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 &lt;em&gt;entrypoint.sh&lt;/em&gt; Bash script provided below was called using the &lt;em&gt;entrypoint&lt;/em&gt; directive.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;I also introduced a few commands to automate most of the stuff that you will have to do when starting a new website.&lt;/li&gt;
&lt;li&gt;This script does the following when launched for the first time:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Creates a fresh Wagtail website using the name provided in &lt;em&gt;.env&lt;/em&gt; file&lt;/li&gt;
&lt;li&gt;Installs the necessary requirements for Wagtail&lt;/li&gt;
&lt;li&gt;Calls the replace_sqlite.py script to change to PostgreSQL &lt;/li&gt;
&lt;li&gt;Runs the initial database migrations required for Wagtail&lt;/li&gt;
&lt;li&gt;Creates the super user for the Wagtail website based on the variables defined in &lt;em&gt;.env&lt;/em&gt; file&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;On subsequent starts, it will just launch the existing Wagtail website.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Working with VS Code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;At this point, I managed to get the default Wagtail site up and running, but open VS Code and face with this issue :( 
! &lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/axw79zv29cv3li1rbpap.png" rel="noopener noreferrer"&gt;Squiggles due to missing Python environment&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;Dev Containers&lt;/em&gt; and the &lt;em&gt;Remote Development&lt;/em&gt; extensions came in handy to achieve this. Below is the basic &lt;em&gt;devcontainer.json&lt;/em&gt; configuration that helped me with this.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "name": "Docker Wagtail Web Workspace",
    "dockerComposeFile": "../compose.yaml",
    "service": "web",
    "workspaceFolder": "/app/mysite"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;And with this when I open &lt;em&gt;Reopen in Container&lt;/em&gt; the Squiggles are gone!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;! &lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tdz52rs3drvmu6uqbfq7.png" rel="noopener noreferrer"&gt;Working VS Code with the environment from Container&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;While I'm still learning and experimenting with Wagtail and Python, I was happy to learn and create a fully self-contained working environment&lt;/li&gt;
&lt;li&gt;This will allow anyone to share their Wagtail project easily without having to give any specific instructions on setting this up.&lt;/li&gt;
&lt;li&gt;Apart from that, with &lt;em&gt;Dev Containers&lt;/em&gt; it is also going to be easier to work with any Containers that are deployed remotely too&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All of the code referenced in this article is available in my repo (Wagtail-Docker) [&lt;a href="https://github.com/shariharan1/wagtail_docker" rel="noopener noreferrer"&gt;https://github.com/shariharan1/wagtail_docker&lt;/a&gt;], feel free to pull/edit/customize to your needs. Let me know in the comments if any feedback or queries.&lt;br&gt;
 &lt;/p&gt;
&lt;h3&gt;
  
  
  References
&lt;/h3&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.accordbox.com/blog/dockerizing-wagtail-app/" rel="noopener noreferrer"&gt;Dockerizing Wagtail&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://learnwagtail.com/tutorials/how-install-wagtail-docker/" rel="noopener noreferrer"&gt;Install Wagtail in Docker&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>wagtail</category>
      <category>docker</category>
      <category>vscode</category>
      <category>newbie</category>
    </item>
  </channel>
</rss>
