Skip to content
Starters

Django starter

Start a Django project with django-admin, then add gunicorn, WhiteNoise, settings from variables and a small tanzahost.json to run it on Tanzahost.

Updated 17 Oct 20264 min read

Start with django-admin startproject, then add four packages, change a few lines of settings.py so it reads its secrets from variables, and add a tanzahost.json that collects static files and runs migrations. With gunicorn in your requirements, we start your app with it by ourselves.

1. Make the project with django-admin

Django 6.1 needs Python 3.12 or newer. As Django's own tutorial shows:

Terminal
python -m pip install Django
django-admin startproject mysite djangotutorial
cd djangotutorial

That makes a folder djangotutorial with manage.py and a project called mysite. We read manage.py to find mysite, and start gunicorn mysite.wsgi:application on the port in PORT.

2. List your packages

Add requirements.txt at the top of the folder:

text
Django>=6.1,<6.2
gunicorn
whitenoise
psycopg[binary]

gunicorn serves your app, WhiteNoise serves its static files, and psycopg talks to PostgreSQL. Add a file .python-version with the version to build with:

text
3.13

3. Read settings from variables

In mysite/settings.py, add import os at the top, then replace the lines for SECRET_KEY, DEBUG, ALLOWED_HOSTS and DATABASES with these:

python
DEBUG = os.environ.get("DEBUG") == "1"
SECRET_KEY = os.environ["SECRET_KEY"] if not DEBUG else os.environ.get("SECRET_KEY", "django-insecure-local-only")
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "localhost,127.0.0.1").split(",")

# Visitors arrive over https through Tanzahost's front door.
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

if os.environ.get("DB_HOST"):
    DATABASES = {
        "default": {
            "ENGINE": "django.db.backends.postgresql",
            "HOST": os.environ["DB_HOST"],
            "PORT": os.environ["DB_PORT"],
            "NAME": os.environ["DB_DATABASE"],
            "USER": os.environ["DB_USERNAME"],
            "PASSWORD": os.environ["DB_PASSWORD"],
            # The connection pool works one transaction at a time.
            "DISABLE_SERVER_SIDE_CURSORS": True,
        }
    }
else:
    DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": BASE_DIR / "db.sqlite3"}}

Then add WhiteNoise, as its own guide shows. In MIDDLEWARE, right after SecurityMiddleware:

python
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    # the rest as it was
]

And at the bottom of the file:

python
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
    "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
    "staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"},
}

On your computer, run it with DEBUG=1 python manage.py runserver.

4. Add tanzahost.json

This tells us the two commands Django needs, and an address to check before visitors arrive. Put it at the top of the folder, next to manage.py:

json
{
  "build": "python manage.py collectstatic --noinput",
  "release": "python manage.py migrate --noinput",
  "health": "/admin/login/"
}
  • build gathers your static files into staticfiles, once, while we build the app.

  • release is the "before going live" command: migrations run once per deploy, before the switch. If they fail, the version before keeps serving.

  • health is the page a new version must answer before it gets visitors. A new project has no page at /, so we check Django's admin login.

5. Push it to GitHub

Add a .gitignore with db.sqlite3, staticfiles/, __pycache__/ and .env, then:

Terminal
git init
git add .
git commit -m "First version"
git branch -M main
git remote add origin https://github.com/asha/duka-api.git
git push -u origin main

6. Add it as an app, with its database

  1. On your project's page, in the Add a service card, choose App.

  2. Give it an App name, like api.

  3. Under Where's your code?, choose GitHub, then your repository and the branch main, and choose Deploy.

This first build stops, because the app has no secret key yet. Nothing was live, so nothing broke. Now give it a database:

  1. In Add a service, choose Database, then PostgreSQL.

  2. Leave Add its connection details to your app's variables ticked, and add it.

Your app gets DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME and DB_PASSWORD, through connection pooling. Then make a secret key on your computer:

Terminal
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"

Open the project's Variables tab, choose your app, then Paste a .env file, and paste these, with your own key:

text
SECRET_KEY=…
ALLOWED_HOSTS=*

Last, on the app's Code card, choose Build again. The build installs your packages with pip and collects the static files, the migrations run, and the app goes live on its free address once /admin/login/ answers. Every push to main then deploys by itself, without downtime.

Why ALLOWED_HOSTS can be *

Django checks the host of each request, to stop requests meant for another site. Our front door already does that: it sends your app only the visits for its own addresses. Our health check calls your app by its private address, which no list of host names would hold, so * is what lets it through. To keep a list of your addresses instead, give the health check a path of its own: DisallowedHost shows how, and Deploy Django does it that way.

What's next

  • Make an admin user in the browser's terminal: open your app, choose Terminal, and run python manage.py createsuperuser.

  • Run Celery or another queue as a worker, in the app's Processes: Processes.

  • Read Deploy Django for the whole walkthrough, and PostgreSQL for the pool and the direct address.