DEV Community

Pedro Campos
Pedro Campos

Posted on • Updated on

Django project - Part 1 Docker

Introduction

This is advanced content on how I structure my django for a new project. I've skipped the basic startproject and startapp, there are plenty of content on that. So, the project started with Poetry, django, .gitignore, git repo, simple home page and django-allauth.

This is for small/medium applications, like a startup/ or small business. After the business grows, you need to evolve the architecture.

The source code from this part

In this series of posts we are going to set up a django project for an efficient development environment(in my opinion). In the end, we are going to have a setup with:

This is a serie with 8 parts, this is the first one.

Take note that not all parts are published yet, it's an ongoing work.

  • Part 1: Docker setup. Simple dockerization, even before Postgres configuration.
  • Part 2: Postgres. Configure Postgres to be used in Django and the configuration in the compose.yml.
  • Part 3: Continuos integration. Add Pytest, ruff, and run it on github action on PR to prevent broken code and ensure code quality.
  • Part 4: HTMX, TailwindCSS and AlpineJS. Configure HTMX in the project, there is a few tricks for django. Configure nodejs on the container and the tailwind watcher in development. Configure AlpineJS, more simple than HTMX and TailwindCSS.
  • Part 5: Dev Tools, Add tools like Marimo, django-extensions, django-toolbar, django-browser-reload.
  • Part 6: Continuos Delivery. Configure staticfiles, in this case with whitenoise. Deploy on fly.io and neon.tech and, of course, using github action for that.

Part 1 Docker setup

I'm not going to explain what Docker is and why you should use it. I assume you know the basics.

What do we need to start?

For now, install the Docker and Just and, of course, our django project with already a simple home page, in this case using poetry.

Just encapsulate commands for easier use. Here is an example of a justfile:

build:
  docker compose build
runserver:
  docker compose up --build
mng command:
  docker compose run --rm web python manage.py {{command}}
sh:
  docker compose run --rm web sh
Enter fullscreen mode Exit fullscreen mode

and then we can run
$ just build
$ just runserver
$ just mng createsuperuser

Ok, let's create the files.

Don't worry, I'll explain each one of them.

First create all those empty files:

  • docker/dev/Dockerfile - Steps to create the image
  • docker/dev/start - Bash file to start the container through entrypoint on docker-compose
  • compose.yml - Simple orchestration of the containers, just one for now.
  • .env - Our environment variables injected into the container by compose.yml, add on .gitignore.
  • .env.template - Same key as .env but with empty values for reference, this one stays on the repo.
  • justfile - Wrapper for the commands.
  • README.md - You know why, don't play dead.

Install django-environ

django-environ - To manage env var
$ poetry add django-environ

The Dockerfile file

# Pull official base image
FROM python:3.12.6-alpine3.19
# Set working directory in the image
WORKDIR /app

# Set env variables
# Don't write out pyc files
ENV PYTHONDONTWRITEBYTECODE 1
# No buffering stdin/stdout
ENV PYTHONUNBUFFERED 1

# update the alpine linux
RUN apk update
# install bash on image, alpine uses ash, but we have a script that uses bash
RUN apk add --no-cache bash

# Copy our poetry artifacts to the building image
COPY poetry.lock pyproject.toml /app

RUN pip3 install poetry
# No need to create a virtual env in the container
RUN poetry config virtualenvs.create false
# Install dependencies with the dev dependecies
RUN poetry install --with dev

# Copy start bash script with the instruction on how to start and serve Django.
COPY ./docker/dev/start /start
RUN sed -i 's/\r$//g' /start
RUN chmod +x /start

# Copy all project files to the image.
COPY . /app

# Not used, the app are going to be running through docker compose
CMD ["manage.py", "runserver", "0.0.0.0:8000"]
Enter fullscreen mode Exit fullscreen mode

start

#!/bin/bash

set -o errexit
set -o pipefail
set -o nounset

# Apply migrations if has a new one.
echo "Running migrations..."
python manage.py migrate

# Start the server for development
echo "Starting runserver"
exec python manage.py runserver 0.0.0.0:8000
Enter fullscreen mode Exit fullscreen mode

compose.yml

services:
# The service to be created on docker
  web:
    # how to build the image for this service
    build:
      # Where is the directory to work on...
      context: .
      # ... with what Dockerfile instructions
      dockerfile: ./docker/dev/Dockerfile
    image: palindrome_local
    container_name: palindrome_local
    volumes:
      - .:/app:z
    env_file:
      - .env
    ports:
      - "8000:8000"
    # The bash file to execute, the one created above and added to Dockerfile
    command: /start
Enter fullscreen mode Exit fullscreen mode

justfile

# List all just commands
default:
  just --list

# Build the docker image
build:
  docker compose build

# Run the Django app in development mode
runserver:
  docker compose up --build

# Run manage.py inside the container like createsuperuser
mng command:
  docker compose run --rm web python manage.py {{command}}
Enter fullscreen mode Exit fullscreen mode

.env

DEBUG=True
SECRET_KEY='dev-key-(g!%yi9at$h6$*sz**^ld6+j)r305*=6i^3ho1bq=z@8c#b7ml'
ALLOWED_HOSTS=*
Enter fullscreen mode Exit fullscreen mode

.env.template

Can be the same for now.

DEBUG=True
SECRET_KEY='dev-key-(g!%yi9at$h6$*sz**^ld6+j)r305*=6i^3ho1bq=z@8c#b7ml'
ALLOWED_HOSTS=*
Enter fullscreen mode Exit fullscreen mode

Update settings.py

For now just these settings, the value come from .env file:

import environ

...
env = environ.Env()
SECRET_KEY = env('SECRET_KEY')
DEBUG = env.bool('DEBUG', False)
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')
...
Enter fullscreen mode Exit fullscreen mode

Update README.md

# Palindrome project

Project used to explain my view on a django project architecture

## Tools, libs, etc. Some time related files.

Versions on Poetry.

- [Python](https://www.python.org/) Programming languange
- [django-environ](https://django-environ.readthedocs.io) Manage .envs in Django
- [Poetry](https://python-poetry.org/) Python packaging and dependency management
    - poetry.lock
    - pyproject.toml
- [Django](https://www.djangoproject.com/) Web framework written in Python
- [Docker](https://www.docker.com/) Manage containers for dev environment
    - compose.yaml
    - compose/dev/Dockerfile
    - compose/dev/start
    - .env
- [Just](https://just.systems/) encapsulate commands for easier use
    - justfile

## Dev environment setup

1. Install Just, Docker and Poetry(opcional).
2. Copy .env.example to .env, no need for edition. 
3. Certified that docker is up and running
4. $ just build

## Run the server for development

1. Certified that docker is up and running
2. $ just runserver

You can access on http://0.0.0.0:8000/
Enter fullscreen mode Exit fullscreen mode

Build and run

Well, with your docker running and everything setup, we can now go to part 2: Postgres, configure Postgres on settings, docker etc.

Good luck and remember what happened in 1971.


Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
kalkrop profile image
Anderson Alves

This post will be very helpful.
Waiting for your continuation in part 2.
Thank you.