Quickstart for a Django project with Docker
In this post we will see how to set up quickly a Django project with Docker, so that it will be less painful to set up a CI pipeline on any environment of your choice (AWS, DigitalOcean, etc.).
This is something that should be done as soon as possible when bootstrapping your project - stop doing things that require a set of pre-installed packages only available on your machine. The earlier you do this, the less painful it will be - yourself included!
1. Clone and/or Create a Repository
You will need a repository for your project, right? So, either clone one already existing or create a new one.
You can clone the repo I created for this tutorial so that we can refer to the same code:
$ git clone https://github.com/markon/django-rest-quickstart
2. Create environment with Pipenv
Pipenv is the recommended Python packaging tool.
Install it:
$ pip install pipenv
and create a new environment in your repo:
$ pipenv --three # if you want to use python 3
and verify that everything went fine:
Pipfile found at {...}/tutorials/django-rest-quickstart/Pipfile.
Considering this to be the project home.
If you open the file, you will find the following content:
$ cat Pipfile
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
Essentially, a brand new project! However, we know we want to use Django, so, let’s add some dependencies:
[packages]
psycopg2 = ">=2.7.3.1"
Django = ">=1.11,<2.0"
[requires]
python_version = "3.6"
In this tutorial we don’t care right now about the specific Django version. Whatever is recent enough should be good - however, you should not do that on production! Many things can be updated between two versions, and having such a file checked in implies that whatever recent version will be taken - don’t use version ranges, unless you know what you are doing.
As we want to have deterministic builds, we could use Pipenv lock, that generates a file containing the specific versions we want to use.
$ pipenv lock
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
Updated Pipfile.lock!
This way, we can easily “freeze” the dependencies to a specific version, so that everyone else will install exactly the same dependencies as we do - no more problems like hey, I am using version 1.7.0.1 while you use 1.7.0.4. One version for all of us!
3. Create a Dockerfile
Before starting a Django project, we really want to make sure not to skip this important step - creating a Dockerfile:
FROM python:3.6-alpine
ENV PYTHONUNBUFFERED 1
RUN apk add --repository http://dl-cdn.alpinelinux.org/alpine/v.3.6/main --no-cache postgresql-dev
RUN apk add --repository http://dl-cdn.alpinelinux.org/alpine/v.3.6/main --no-cache gcc
RUN apk add --repository http://dl-cdn.alpinelinux.org/alpine/v.3.6/main --no-cache python3-dev
RUN apk add --repository http://dl-cdn.alpinelinux.org/alpine/v.3.6/main --no-cache musl-dev
RUN mkdir /code
WORKDIR /code
ADD . /code/
RUN pip3 install pipenv
RUN pipenv --three
RUN pipenv install --deploy --system
EXPOSE 8000
CMD ["python", "manage.py", "runserver"]
To summarize what it does:
- Use python:3.6-alpine
- Install PostgreSQL (we will need it for our project)
- Create a new directory /code inside the container and the current directory to it
- Install Pipenv for Python 3
- Expose port 8000 for our Django web service
- Run manage.py runserver by default
4. Create a docker-compose.yaml file
Following the official documentation from Docker, let’s create now a docker-compose file:
version: '3'
services:
db:
image: postgres
web:
build: .
command: python3 manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- db
We mount the current directory as /code, so that we don’t have to build a new Docker image every time we change the code. However, be careful: if you update your dependencies (e.g., a little change in Pipfile), your Docker image needs to be updated, because they come pre-installed.
5. Create a Django project
Now we are finally able to create a Django project! Go to the root of your project
docker-compose run web django-admin.py startproject djangorestapi .
You should now see a djangorestapi
folder and a manage.py
in your project.
6. Start coding your app
Finally, we can add some code to create some features. An example I would like you to refer to is based on the previous post - RBAC with django-rest-framework and django-guardian.
The full code is visible in the repository, as it’s a bit out of scope for this tutorial.
7. Test locally
Normally, you would want to run migrations first.
docker-compose run web python manage.py migrate
then you will be able to run
docker-compose up
and wait until your service is up and running. Then you can play with your app available on http://localhost:8000/api/profiles. Please follow the README file to see how to run the examples.
Note: sometimes you may get a
ConnectionRefused
error, because PostgreSQL is slower than Django to start up. In this case, you could just re-run the command twice.