This is a short introduction how to build a Django app with Poetry as a package manager and then make it work in a Docker container. Finally, there's a how-to for Heroku and an example app you can use as a reference.
Setting up Poetry
Installing poetry is very simple:
pip3 install --user poetry
Poetry uses the
pyproject.toml, a file based on
PEP-518, which is supposed
to be a unified way of specifying project meta information, build system,
dependencies, etc. However, each tool that uses this file can
add its own sections and the mess with Python virtual environments
and package managers continues.
Adding dependencies is as easy as:
poetry add Django
I'd recommend always switching into the venv by
and then running
Now, Poetry uses it's lock file
poetry.lock. This is different
from using plain old
pip, but it's on par
with - for example - Pipenv.
The advantage of using Poetry over virtualenv or Pipenv is the easy interface and the option to build and publish packages on PyPI.
So now we setup the project with Poetry as the package manager of choice and our Django app works locally without problems (hopefully).
gunicorn is a Python WSGI server that we'll use for the Docker image.
You can already install it with Poetry:
poetry add --dev gunicorn
and then run your app using
Configuring static files
Serving static files is a bit more complicated with gunicorn, but once we set up a few things, it'll help us create the Docker image.
settings.py, configure static files like this:
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, '<module name>', 'static'), )
Furthermore, you need to install and enable whitenoise:
poetry add --dev whitenoise
MIDDLEWARE = [ # ... 'whitenoise.middleware.WhiteNoiseMiddleware', # ... ]
Putting everything into Dockerfile
For reference, I will put an example Dockerfile here and then explain how it works.
FROM python:3 WORKDIR /usr/src/app EXPOSE 8000 # Install poetry and deps # Exporting to requirements.txt will avoid hassle with poetry venv COPY pyproject.toml ./ COPY poetry.lock ./ RUN pip install poetry RUN poetry export -f requirements.txt --output requirements.txt RUN pip install -r requirements.txt # Copy project COPY manage.py manage.py COPY <your app> <your app> COPY <your project> <your project> # Collect static files, pass dummy DJANGO_SECRET_KEY # or else ./manage.py will fail # since DJANGO_SECRET_KEY is not available during the build step RUN DJANGO_SECRET_KEY=dummy ./manage.py collectstatic # Run server CMD [ "gunicorn", "-b", "0.0.0.0:8000", "<your project>.wsgi" ]
The steps are already somewhat explained with the comments. In more detail now.
We start with a base python3 image, setup a working directory for the
project source code and expose the port where
gunicorn will be running
(default is 8000).
FROM python:3 WORKDIR /usr/src/app EXPOSE 8000
Then we copy files for Poetry,
COPY pyproject.toml ./ COPY poetry.lock ./
After that, we install Poetry itself, use it to export our dependencies
from the lock file into
requirements.txt and then use
pip to install
those frozen dependencies:
RUN pip install poetry RUN poetry export -f requirements.txt --output requirements.txt RUN pip install -r requirements.txt
Now the environment is setup and we can copy the project source code (actually this step could happen earlier):
COPY manage.py manage.py COPY <your app> <your app> COPY <your project> <your project>
<your project> is the root folder name and
<your app> is each app
inside your project.
Now the part about collecting static files from your apps.
RUN DJANGO_SECRET_KEY=dummy ./manage.py collectstatic
Since this is a build step,
./manage.py cannot reach the environment
variables. They are only reachable once the app is running.
This SO question
sums the problem up. However, we don't need to generate random secret key
as in the accepted answer. What we can do is inside
this Dockerfile example.
Just export a dummy
DJANGO_SECRET_KEY before running
so the script doesn't fail and everything is okay. Since this is a build step
that doesn't in any way interact with the users of your app, it's
perfectly fine to use any dummy value.
And finally we start up the web server:
CMD [ "gunicorn", "-b", "0.0.0.0:8000", "<your project>.wsgi" ]
Now build the image:
docker build -t project-name .
And run it:
docker run --rm -e DJANGO_SECRET_KEY="<secret key>" -p 8000:8000 project-name
Using Poetry on Heroku
For deploying your project on Heroku, you need a
Procfile in your
project's root directory, containing the command to run the server:
web: gunicorn project-name.wsgi
But since Heroku's
heroku/python buildpack only detects
Pipfile, we need some pre-processing to export Poetry's dependencies
This is done by a python-poetry-buildpack.
It needs to run before
heroku/python. This will export
for us, which can be then grabbed by
heroku/python and the deploy process
can continue as with pip or Pipenv.
Feel free to use my daylio-stats project as a reference for building your own cool stuff! Some of the steps described here were pretty difficult to find on the internet and make work.