Unified Django development toolkit for your team using VSCode - Part 1
How to setup a Django project with VSCode dev containers and docker compose
docker compose up -d
is amazing right?
You don't know what is does? In simple words it will create required services to make a specific project up and running.
I will not cover basic knowledge about docker and docker compose, there are plenty of tutorials/videos doing deep introduction for this.
Objectif
We will have a VSCode + Docker compose setup which will run the project in VSCode dev containers, this will make sure that all services are up and running (DB, Django's runserver, Cache ...) We will also setup useful VSCode extensions which will boost productivity and will be available for anyone using the project with VSCode.
Requirements
- Linux (docker + VSCode + VSCode Dev Containers extension)
- Mac OS (Docker Desktop + VSCode Dev Containers extension)
Windows (Docker Desktop + VSCode Dev Containers extension)
- Docker Desktop
- VSCode
- Dev Containers extension
// Mac M1/M2 users please use Apple Silicon version for Docker Desktop and VSCode for a better performance
VSCode dev container (Developing inside a Container)
Why?
- How many time did you have to install virtualenv in your machine to enable full features of the Django project you're working on?
- Wasn't you annoyed of how you will keep them up to date if your team use Docker or/and Docker compose and sync them.
- What about installing system dependencies needed for compilation for some scenarios (python dev, imaging libraries, librdkafka, libmemcached-dev, postgresql headers if not using psycopg2-binary etc)?
- And how much time did you already spend on this and you will spend on each new install of your OS or some new upgrades?
What about the time spent by your team or team members on that and specially when using different OS (Mac, Linux, Windows)?
What about opening VSCode (No terminal is needed) and then everything is setup for you out of the box, you will only need git, docker/docker desktop and that's all !
This will also keep your machine clean from different dependencies, you don't have to switch between dependencies version or system packages version, Dockerize everything!
For a full documentation on setting up dev containers please check this official link. I will later on refer to some section to it.
Dummy Django project
We will start by cloning a dummy Django project which don't do much and try to add Dockerfile
, docker-compose.yml
and dev container setup to it.
git clone --branch initial_project_setup https://github.com/MounirMesselmeni/django-vscode-dev-containers.git
This a simple Django project containing the required setup with Dockerfile
and docker-compose.yml
, we use postgres image for database
Project structure
Dockerfile
FROM python:3.10
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV LC_ALL=C.UTF-8
WORKDIR /app
COPY requirements.txt /app/
RUN pip install -r requirements.txt
docker-compose.yml
version: "3.9"
services:
django:
build:
context: .
dockerfile: ./Dockerfile
command: python manage.py runserver 0.0.0.0:8000
ports:
- 8000:8000
volumes:
- .:/app:cached
depends_on:
- postgres
postgres:
image: postgres:15.0-alpine
user: postgres
environment:
- POSTGRES_USER=app
- POSTGRES_PASSWORD=app
- POSTGRES_DB=app
volumes:
# with named volume we will persist database state E.g after restarting docker/machine
- postgres-data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
postgres-data:
Add a .env
file under django_vscode_dev_containers
folder
DEBUG=yes
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY=django-insecure-qk0&tbbj(&c9x^t6wu6wkwwg@xox^t0*@(dbo3et03x*+1-avy
ALLOWED_HOSTS=localhost,127.0.0.1,::1,0.0.0.0
DATABASE_URL=psql://app:app@postgres:5432/app
Setting up dev containers
We can do that using VSCode command palette and dev containers extension helper but for the sake of simplicity we will do this by hand.
Create a .devcontainer/devcontainer.json file in the main directory
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/docker-existing-docker-compose
// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml.
{
"name": "Django VSCode Dev Containers",
// Update the 'dockerComposeFile' list if you have more compose files or use different names.
// The .devcontainer/docker-compose.yml file contains any overrides you need/want to make.
"dockerComposeFile": [
"../docker-compose.yml",
"docker-compose.yml"
],
// The 'service' property is the name of the service for the container that VS Code should
// use. Update this value and .devcontainer/docker-compose.yml to the real service name.
"service": "django",
// The optional 'workspaceFolder' property is the path VS Code should open by default when
// connected. This is typically a file mount in .devcontainer/docker-compose.yml
"workspaceFolder": "/app",
"runServices": [
"django",
"postgres"
]
// Uncomment the next line if you want to keep your containers running after VS Code shuts down.
// "shutdownAction": "none",
// Uncomment the next line to run commands after the container is created - for example installing curl.
// "postCreateCommand": "apt-get update && apt-get install -y curl",
// Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root.
// "remoteUser": "vscode"
}
Note on runServices, we define here which services we want VSCode to make sure they are up and running, we could have a redis service which we don't need, or it's essential to the proper functioning of the application so we would like VSCode to take care of running it with docker/docker-compose.
And devcontainers/docker-compose.yml
version: '3.9'
services:
# Update this to the name of the service you want to work with in your docker-compose.yml file
django:
# If you want add a non-root user to your Dockerfile, you can use the "remoteUser"
# property in devcontainer.json to cause VS Code its sub-processes (terminals, tasks,
# debugging) to execute as the user. Uncomment the next line if you want the entire
# container to run as this user instead. Note that, on Linux, you may need to
# ensure the UID and GID of the container user you create matches your local user.
# See https://aka.ms/vscode-remote/containers/non-root for details.
#
# user: vscode
# Overrides default command so things don't shut down after the process ends.
command: /bin/sh -c "while sleep 1000; do :; done"
Open the Command Palette (Ctrl/Cmd+shift+p) and search for Rebuild and reopen in Container
Now VSCode will open the project inside the container, we can see the docker building command and output by clicking on Starting Dev container show logs
The logs would look like this
We know that we are developing inside a container in the bottom left corner we can see the name we provided in devcontainer.json
file
Setting up python extensions
Now we need more productivity by setting up proper python extensions to use code completion etc with Pylance.
Let's add required extensions list into the .devcontainers/devcontainer.json
and some custom vscode setting (Yes you can define VSCode settings in dev container and share them with others)
"settings": {
"python.terminal.activateEnvironment": false,
"python.terminal.activateEnvInCurrentTerminal": true,
"python.pythonPath": "/usr/local/bin/python",
"python.defaultInterpreterPath": "/usr/local/bin/python",
"python.languageServer": "Pylance",
// file formatting options
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
// files to exclude from all checks
"files.exclude": {
"**/*.pyc": true,
"**/.git": false,
"**/migrations/*": false
},
"python.analysis.extraPaths": [
"/usr/local/lib/python3.10/site-packages/"
],
"python.analysis.useImportHeuristic": true,
"python.analysis.autoSearchPaths": true
},
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"aaron-bond.better-comments",
"visualstudioexptteam.vscodeintellicode",
"mikestead.dotenv"
]
Command Palette and search for Rebuild to rebuilt the container and restart:
Now we can see VSCode is installing the extensions inside the dev container
A common problem to this, that sometime Pylance does not work properly from the first time and you need to restart or rebuild, and everytime you add a requirement or change dockerfile VSCode will re-install all the extensions. To fix this we can avoid VSCode extensions reinstalls by adding a persisted volume for those extensions.
Let's modify our Dockerfile to create required extensions folder
FROM python:3.10
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV LC_ALL=C.UTF-8
# Yes we are using the root user now, it's not encouraged but this is only for demo purposes
RUN mkdir -p /root/.vscode-server/extensions /root/.vscode-server-insiders/extensions
WORKDIR /app
COPY requirements.txt /app/
RUN pip install -r requirements.txt
And add the new docker volume in the .devcontainer/docker-compose.yml
version: '3.9'
services:
# Update this to the name of the service you want to work with in your docker-compose.yml file
django:
# If you want add a non-root user to your Dockerfile, you can use the "remoteUser"
# property in devcontainer.json to cause VS Code its sub-processes (terminals, tasks,
# debugging) to execute as the user. Uncomment the next line if you want the entire
# container to run as this user instead. Note that, on Linux, you may need to
# ensure the UID and GID of the container user you create matches your local user.
# See https://aka.ms/vscode-remote/containers/non-root for details.
#
# user: vscode
# Overrides default command so things don't shut down after the process ends.
command: /bin/sh -c "while sleep 1000; do :; done"
volumes:
- vscode_extensions:/root/.vscode-server/extensions
volumes:
vscode_extensions:
Now rebuild again using commands palette
We can re-run Rebuild without Cache just to test out if the extensions changes are working properly
Now we don't re-install extensions as they are persisted in the docker volume
We should be able now to have autocompletion working and navigating/going to third party packages like environ
in settings.py with Cmd/Ctrl click
To go inside the container (Our case the django/python one) you click in the + button as shown bellow
We can run our runserver from there
./manage.py runserver 0:8000
And access localhost:8000
End
Nice what do you think?
This might looks like a lot of work, but you will set it up once and after it's mostly copy paste and you have your environment easy to work with your colleagues/teammates and simplify the process.
Final result could be find in the github repo:
Part 2 (Next)
- I will add a part 2 soon in which we will cover how to use git from inside the container, this is handy if you want to have some pre-commit scripts and you have no interest to install that on the host machine or if you like using the git integration inside VSCode.
- Setup automatic formatting using black - On save
- Setup automatic imports sorting using isort - On save
- Pylint/Flake8 previews inside VSCode