Setup a Django project with Docker
Tags: django, docker, python, tutorial
Published: 2022-04-13 02:24:35

## :snake:

We all know that Django is great for applications even if we want to, with Docker the conversation gets better!

How about I show you an initial Setup for your projects that can not only speed up your development, but also make your environment advanced and complete for your applications?

Interested? So read on and leave the rest to me.

## Requirements

- Python
- Docker

## Starting ✅

We are going to use a test application to create the development environment, but remember that this setup applies to any project.

Just be mindful of the changes you make.

**1 - First, we start a venv enviroment**

```bash
python -m venv venv && source ./venv/bin/activate
```

**2 - Now we install our dependencies ( and don't worry, I'll tell you what each one will do )**

```bash
pip install django black python-dotenv psycopg2-binary
```

And don't forget to create the dependency files:
```bash
pip freeze > requirements.txt
```

- [Django](https://www.djangoproject.com/) --> Our main framework
- [black](https://github.com/psf/black) --> Python code formatter
- [python-dotenv](https://pypi.org/project/python-dotenv/) --> Python-dotenv reads key-value pairs from a `.env` file and can set them as environment variables.
- [psycopg2-binary](https://pypi.org/project/psycopg2-binary/) --> most popular PostgreSQL database adapter for the Python 

### Tips:

- For MySQL, MariaDB and derivatives types, use `mysqlclient`

**3 - Let's init our app:**

```bash
django-admin startproject your_fantastic_name .
```

**4 - Creating the `.env` file:**

Go to the root of your workspace and create the `.env` file with this values:

```env
# App

SECRET_KEY=your_ultra_secret_key
# 1 = True or 0 = False
DEBUG=
# localhost 127.0.0.1 0.0.0.0 ...
HOSTS=

# POSTGRES
POSTGRES_DB=
POSTGRES_USER=
POSTGRES_PASSWORD=

# PGADMIN
PGADMIN_DEFAULT_EMAIL=
PGADMIN_DEFAULT_PASSWORD=
```
![Like my env file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/werugwiaty8vb98dk3hq.png)
 

*later these values ​​will be useful*

**5 - Configuring our application:**

First let's go to the core of our application, located in the `settings.py` file and we will add some imports

By default the Path is already imported, but to use our `python-dotenv` package let's add two more imports:


```py
# settings.py
from pathlib import Path
from dotenv import load_dotenv

import os
```

And just below we will use `os` to configure the project:

```py
# settings.py

load_dotenv()
BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = os.getenv("SECRET_KEY")
DEBUG = int(os.getenv("DEBUG", default=0))
ALLOWED_HOSTS = os.getenv("HOSTS").split(" ")
```

### Tips 2:

- For the people who gonna use Django with html and css, i like to put all the static files into the **templates** folder

```py
# settings.py
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [os.path.join(BASE_DIR, "templates")],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ]
        },
    }
]
``` 

- For who gonna use *Rest-Framework*, here is my configs for the app:

```py
# settings.py
REST_FRAMEWORK = {
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
    "PAGE_SIZE": 5,
    "DEFAULT_RENDERER_CLASSES": ["rest_framework.renderers.JSONRenderer"],
    "DEFAULT_PARSER_CLASSES": [
        "rest_framework.parsers.JSONParser",
        "rest_framework.parsers.FormParser",
    ],
    "DEFAULT_THROTTLE_CLASSES": [
        "rest_framework.throttling.AnonRateThrottle",
        "rest_framework.throttling.UserRateThrottle",
    ],
    "DEFAULT_THROTTLE_RATES": {"anon": "15/min", "user": "20/min"},
}
```

**6 - Setting up the Database in Django:**

In this context and I will exclusively use postgres, to configure the connection just modify the DATABASE object with the following values:

```py
# settings.py
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": os.getenv("POSTGRES_DB"),
        "USER": os.getenv("POSTGRES_USER"),
        "PASSWORD": os.getenv("POSTGRES_PASSWORD"),
        "HOST": "db",
        "PORT": 5432,
    }
}
```

### More Tips:

- This is optional but recomendy, for storage the Static Files and some other configs for language and more:


```py
# settings.py
LANGUAGE_CODE = "pt-BR"
TIME_ZONE = "America/Sao_Paulo"
USE_I18N = True
USE_TZ = True

STATIC_URL = "static/"
STATICFILES_DIRS = (os.path.join(BASE_DIR, "templates/static"),)
STATIC_ROOT = os.path.join("static")

MEDIA_URL = "media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
```

![Yep settings done](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ts5fa063f3h23qr0abq4.png)
 
<br>
## **Docker Setup :ocean:**

First we need the build for our app, let's create him!

```Dockerfile
# Image of python with Debian
FROM python:3.9-bullseye

# Set Workdir
WORKDIR /usr/src/app

# Envs for don't generate pycache
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Some important libs
RUN apt update && apt upgrade -y \
    && apt install gcc python3-dev musl-dev bash build-essential libssl-dev libffi-dev -y

# Upgrade pip, copy requirements and install all the project dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt

# Entrypoint gonna be useful when we up the container
COPY entrypoint.sh .
RUN sed -i 's/\r$//g' /usr/src/app/entrypoint.sh
RUN chmod +x /usr/src/app/entrypoint.sh

# Copy all the files for the root dir
COPY . .

ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
```

Now we need our compose file to up all the tools and our app:

```yml
version: "3"

services:
  web:
    build: .
    container_name: "web-app-server"
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/usr/src/app/
    ports:
      - 8000:8000
    networks:
      - postgres
    env_file:
      - ./.env
    depends_on:
      - db

  db:
    image: postgres:13.0-alpine
    container_name: "postgres-db-server"
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
    networks:
      - postgres
    env_file:
      - ./.env

  pgadmin:
    container_name: "pgadmin-django-server"
    image: dpage/pgadmin4
    environment:
      - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL}
      - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_DEFAULT_PASSWORD}
    volumes:
      - pgadmin:/root/.pgadmin
    ports:
      - "5050:80"
    networks:
      - postgres
    env_file:
      - ./.env
    depends_on:
      - db


volumes:
  postgres_data:
  pgadmin:

networks:
  postgres:
    driver: bridge
```

If you notice, our compose will be reading our values ​​that we defined in the .env file, like database name, username, password and so on. 

If you are not sure which values ​​are defined in the file, run the command `docker-compose config` in the terminal, it will display the compose with all the values.

![command result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p26jtf07p5dlh4w058m9.png)
 

And finally, our entrypoint file

```sh
#!/bin/sh

# entrypoint.sh

# Verify if the database is connected
if [ "$DATABASE" = "postgres" ]
then
    echo "Waiting for postgres..."

    while ! nc -z $SQL_HOST $SQL_PORT; do
      sleep 0.1
    done

    echo "PostgreSQL started"
fi

# Delete all the tables
python manage.py flush --no-input
# Do a new migrate
python manage.py migrate

exec "$@"
```
### Yea, more tips 😄:

- For security, add the file `.dockerignore` for exclude some folders in the build:

```.dockerignore
venv
```

## **Now let's start our app 🎯**

All you will need is just to give a little command:

```bash
docker-compose up
```
![Log of the containers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7f36zdrgdim1yxah560q.png)
 

And that's it, you will have uploaded all 3 containers, the database, pgAdmin and your own application, all in a setup that is easy to customize and implement new tools.

In case you are in doubt, "how can I access my application with docker?", simple, with Docker you can do everything!

First type:

```bash
docker ps
```
Take the id of your container and:

```bash
docker exec -it id_of_container bash
```

![Example of docker exec](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3grxkw847wvkvqyh0m60.png)
 

And that's it, you can develop your application in a simple way :)

Now we can see the result!

![Django Admin of Rest-Framework](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/80iffdp3qs8d4t4tfbpf.png)
 
I hope that with this tutorial I have helped you to make your Django applications with a special touch.

See you next time!