Django 4: Adding a database to our stack
Building a Multiplayer Tic Tac Toe with Python, Docker, and AI, chapter 4
Hi everyone!
Now, we can serve both dynamic and static files, and we have a single entry point for our app. The next step is to add a DB engine to our stack.
Let 's get started!
Articles in this series
Chapter 1: Let the journey start
Chapter 2: Create a containerized Django app with Gunicorn and Docker
Chapter 3: Serve Django static files with NGINX
Chapter 4: Adding a database to our stack
Chapter 5: Applications and sites
Why PostgreSQL/TimescaleDB?
I choose TimescaleDB (a PostgreSQL extension for time series) for the following reasons:
I'm already familiar with PostgreSQL, an almost indestructible DB engine, tested in production environments for years.
I want to learn how to use the TimescaleDB extension
Because... why not?
Add TimescaleDB container
Create a new directory for the new container:
cd tic-magical-line
mkdir db
Inside the db folder, create the same three files than in other containers:
cd tic-magical-line/db
touch Dockerfile
touch environment.env
touch requirements.txt
Write environment.env
required variables (write your own user/password, this is used only for demo purposes):
POSTGRES_USER=tic
POSTGRES_PASSWORD=drag0n
# Use the tic user as the default postgres user for internal tasks
PGUSER=tic
Write the following content in Dockerfile
:
FROM timescale/timescaledb-ha:pg16
Add the container to docker-compose file
Modify the docker-compose file adding dependencies between containers and the new TimescaleDB container. We are also adding a health-check to the database container:
version: "3.8"
services:
app:
build:
context: ./
dockerfile: ./app/Dockerfile
container_name: app
hostname: app
restart: always
volumes:
- ./src/:/usr/src/app/
depends_on:
db:
condition: service_healthy
env_file:
- ./app/environment.env
networks:
django_net:
ipv4_address: 10.20.30.1
server:
build:
context: ./
dockerfile: ./server/Dockerfile
container_name: server
hostname: server
restart: always
depends_on:
db:
condition: service_healthy
env_file:
- ./server/environment.env
ports:
- 8080:8080
networks:
django_net:
ipv4_address: 10.20.30.2
db:
build:
context: ./
dockerfile: ./db/Dockerfile
container_name: db
hostname: db
restart: always
env_file:
- ./db/environment.env
volumes:
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: [ "CMD-SHELL", "pg_isready", "-d", "ticmagicalline" ]
interval: 30s
timeout: 10s
retries: 3
networks:
django_net:
ipv4_address: 10.20.30.3
networks:
django_net:
ipam:
config:
- subnet: 10.20.30.0/24
gateway: 10.20.30.254
As you can see, one of the lines of the DB container specifies an SQL file to execute at container start. This is very useful to initialize the DB. We need to create the database that the Django app will use.
Create the SQL file:
cd tic-magical-line/db
touch init.sql
Write the following content:
CREATE DATABASE ticmagicalline;
Configure the database in Django
Edit settings.py
inside Django app source code folder, and set the following variables to configure the database backend:
DATABASES = {
'default': {
"ENGINE": "django.db.backends.postgresql",
"NAME": "ticmagicalline",
"USER": "tic",
"PASSWORD": "drag0n",
"HOST": "10.20.30.3",
"PORT": "5432",
}
}
NOTE: Write your chosen password in this code block
After configuring the backend and creating the database, we need to populate it with the default tables and data. This is done with the manage.py
command, inside the container:
Get the Django app container ID:
docker ps | grep tic-magical-line_app
Enter in the container shell:
docker exec -it 5e1d486aff24 /bin/bash
Go to app folder:
cd /
Execute migration:
python manage.py migrate
Exit from the shell:
exit
Also, we need to create an admin user:
Get the Django app container ID:
docker ps | grep tic-magical-line_app
Enter in the container shell:
docker exec -it 5e1d486aff24 /bin/bash
Go to app folder:
cd /
Create user:
python manage.py createsuperuser
Exit from the shell:
exit
We can automate all these changes using the main app Dockerfile
. Edit it with this content:
FROM python:3.12.2-bookworm
# set some environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install dependencies
RUN pip install --upgrade pip
COPY ./app/requirements.txt .
RUN pip install -r requirements.txt
COPY ./app/entrypoint.sh .
RUN chmod +x entrypoint.sh
# copy project
COPY ./app/src/ticmagicalline .
ENTRYPOINT [ "/entrypoint.sh" ]
Then, create the new entrypoint.sh file: with:
#!/bin/sh
echo "Executing Django DB migration..."
python manage.py migrate
echo "Creating Django administrator..."
python manage.py createsuperuserwithpassword \
--username $DJANGO_ADMIN_USERNAME \
--password $DJANGO_ADMIN_PASSWORD \
--email $DJANGO_ADMIN_EMAIL \
--preserve
echo "Initial tasks finalized!"
# Execute gunicorn and wait
gunicorn ticmagicalline.wsgi:application --bind 0.0.0.0:8081
Add the new variables to environment.env file:
DJANGO_ADMIN_USERNAME=tic
DJANGO_ADMIN_EMAIL=tic@fake.com
DJANGO_ADMIN_PASSWORD=drag0n
And add the new dependency to requirements.txt:
Django
gunicorn
psycopg2-binary
django-createsuperuserwithpassword
Don't forget to edit the Django app settings to enable the new createsuperuserwithpassword extension adding the following line to settings.py:
INSTALLED_APPS += ("django_createsuperuserwithpassword", )
Rebuild the images and execute again
After all these changes, we need to rebuild the new images with:
docker-compose stop
docker-compose build
After the data population, start the cluster again with:
docker-compose up
Now, we can test the database integration login into the Django admin site, with this link:
http://localhost:8080/admin/login/?next=/admin/
Use the username and password defined in the previous step. And voilá! We are now inside our Django app admin site!
About the list
Among the Python and Docker posts, I will also write about other related topics (always tech and programming topics, I promise... with the fingers crossed), like:
Software architecture
Programming environments
Linux operating system
Etc.
If you found some interesting technology, programming language or whatever, please, let me know! I'm always open to learning something new!
About the author
I'm Andrés, a full-stack software developer based in Palma, on a personal journey to improve my coding skills. I'm also a self-published fantasy writer with four published novels to my name. Feel free to ask me anything!