Skip to content

achingachris/django-starter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Django Project Starter Template

Django (Server Only) starter template, inherited from SaaS Pegasus.

This project runs in two distinct modes:

Mode How it runs Database Cache / broker Celery Used for
Local Natively on your machine via uv + npm SQLite (db.sqlite3) DummyCache (no Redis needed) Eager (synchronous) Day-to-day development
Production Docker Compose (Postgres + Redis + web + Vite + Celery) Postgres Redis Real worker + beat Containerized / deployed stack

The split is driven by DEBUG and a few environment variables — see Configuration. You do not need Docker for everyday development.


Table of contents


Local development

Local development runs natively on your Python environment (managed with uv) against a SQLite database. No Docker, Postgres, or Redis required.

Prerequisites

Install these once on your machine:

  • uv — manages the virtual environment and provisions Python 3.14 for you (you do not need to install Python 3.14 separately):
    curl -LsSf https://astral.sh/uv/install.sh | sh   # or: brew install uv
  • Node.js 20+ — provides node and npm for the front-end build.
  • make — preinstalled on macOS/Linux. On Windows, follow these instructions.

1. Bootstrap (make init)

From the project root:

make init

This single command:

  1. Copies .env.example.env (only if .env doesn't already exist).
  2. Installs Python dependencies into a local .venv with uv sync.
  3. Installs front-end dependencies with npm install.
  4. Creates the SQLite database (db.sqlite3) and applies all migrations.

2. Admin user (automatic)

In DEBUG, make start (i.e. runserver) automatically creates a development superuser the first time it runs, then silently skips it afterwards. Defaults:

Field Value
Email admin@example.com
Password admin

Override via DEV_SUPERUSER_EMAIL / DEV_SUPERUSER_PASSWORD in .env. This only happens when DEBUG is true, so production is never affected. You can still create more accounts with make manage ARGS='createsuperuser'.

3. Run the app (two processes)

Local development needs two terminals running simultaneously — the Django server and the Vite front-end server. In DEBUG, CSS/JS are served by Vite, so if you skip the second process the page loads unstyled.

Terminal 1 — Django backend (port 8000):

make start

Terminal 2 — Vite front end (port 5173):

make npm-dev

4. Open it

Visit http://localhost:8000 — you should see a fully styled page. The Django admin is at http://localhost:8000/admin/ (log in with the auto-created dev superuser from step 2).

Styles not loading? It almost always means the Vite dev server (step 3, Terminal 2) isn't running, or node_modules is missing. Run make npm-install then make npm-dev and refresh.

New to Pegasus? Try these next steps.


Everyday commands

Run make with no arguments to list every available target. Local targets (start, migrate, test, shell, …) run natively via uv; production targets are prefixed with prod- and use Docker.

Command What it does
make start Run the Django dev server
make shell Open a Django Python shell
make dbshell Open a database shell (SQLite locally)
make manage ARGS='<cmd>' Run any manage.py command, e.g. ARGS='createsuperuser'
make migrations Create new migrations
make migrate Apply migrations
make test Run the test suite
make ruff Format and lint Python with Ruff
make npm-dev Run the Vite dev server
make npm-build Build production front-end assets

Management commands can also be run directly: uv run python manage.py <command>.


Database

Local development uses SQLite by default — no setup required. Leaving DATABASE_URL unset makes Django use the db.sqlite3 file in the project root.

make migrations   # uv run python manage.py makemigrations
make migrate      # uv run python manage.py migrate

In production, set DATABASE_URL (e.g. a Postgres connection string) in the environment — it takes precedence over SQLite automatically.


Front end

JavaScript/TypeScript lives in assets/ and is bundled by Vite and served through django-vite. Tailwind v4 + DaisyUI provide styling.

make npm-install            # install all npm packages
make npm-install <package>  # install a specific package
make npm-dev                # Vite dev server with hot reload (local development)
make npm-build              # build optimized assets (production)
make npm-type-check         # TypeScript type checking

In DEBUG, DJANGO_VITE["default"]["dev_mode"] is True, so assets are served live from the Vite dev server. With DEBUG=False, Django serves the built manifest from npm-build instead.


Generating the API client

The REST API (Django Rest Framework) publishes an OpenAPI schema via drf-spectacular at /api/schema/. The TypeScript client in api-client/ is generated from that schema and consumed by the front end (see assets/javascript/api.js). It is generated code — don't edit it by hand.

Whenever you add or change API endpoints, regenerate the client:

make generate-api-client

This target:

  1. Exports the OpenAPI schema from Django (manage.py spectacular).
  2. Runs OpenAPI Generator (typescript-fetch), pinned to the version in OPENAPI_GENERATOR_VERSION (kept in sync with api-client/.openapi-generator/VERSION), via npx @openapitools/openapi-generator-cli.
  3. Writes the result into api-client/ and cleans up the temporary schema file.

Requirements: node/npx and a Java runtime (the generator runs on the JVM). Review the diff before committing.


Celery & background tasks

Celery runs background and scheduled tasks. Locally, tasks run eagerly (synchronously) by default (CELERY_TASK_ALWAYS_EAGER defaults to DEBUG), so no broker is required.

To exercise the real worker locally, run a Redis instance, set REDIS_URL in .env, then:

make celery
# or directly:
uv run celery -A config worker -l INFO --beat --pool=solo

The solo pool is fine for development but not for production.

In production (DEBUG=False) tasks dispatch to the Redis broker and are processed by the dedicated celery container (see Production).


Testing

make test                                              # run everything
make test ARGS='apps.web.tests.test_basic_views'       # a single module
make test ARGS='apps.web.tests.test_basic_views --keepdb'  # reuse the test DB (faster)

On Linux you can re-run tests on change:

find . -name '*.py' | entr uv run python manage.py test apps.web.tests.test_basic_views

Code quality & git hooks

make ruff           # ruff format + lint --fix
make ruff-format    # format only
make ruff-lint      # lint + autofix only
make type-check     # mypy

Install the pre-commit hooks (run automatically on every commit):

uv run pre-commit install --install-hooks

See the Pegasus code-formatting docs for details.


Configuration

Configuration is read from environment variables (via .env locally). make init / make setup-env copy .env.example.env for you. The most important variables:

Variable Default Purpose
SECRET_KEY insecure dev key Django secret key. Set a strong value in production.
DEBUG True Debug mode. Must be False in production (or use config.settings.prod).
ALLOWED_HOSTS * Comma-separated allowed hosts. Restrict in production.
DATABASE_URL (unset → SQLite) Postgres connection string in production.
REDIS_URL (unset → redis://localhost:6379/0) Cache + Celery broker. Required in production.
CELERY_TASK_ALWAYS_EAGER = DEBUG Run tasks synchronously when true.
DEV_SUPERUSER_EMAIL / DEV_SUPERUSER_PASSWORD admin@example.com / admin Auto-created dev superuser (DEBUG only).
ENABLE_DEBUG_TOOLBAR False¹ Django Debug Toolbar (disabled during tests).
EMAIL_BACKEND console backend Email backend; configure a real one (e.g. Mailgun/Anymail) in production.
DJANGO_PORT / DJANGO_VITE_PORT 8000 / 5173 Dev server ports.
POSTGRES_PORT / REDIS_PORT 5432 / 6379 Docker service ports.
TURNSTILE_KEY / TURNSTILE_SECRET (empty) Cloudflare Turnstile keys (optional).
GOOGLE_ANALYTICS_ID (empty) GA measurement ID (optional).

¹ .env.example ships with ENABLE_DEBUG_TOOLBAR=True for convenience.

Never commit .env — it's git-ignored. See .env.example for the full, annotated list.

Settings modules: live in the config/settings/ package.

  • config.settings.base — shared settings imported by both environment modules. Not selected directly.
  • config.settings.dev — the default, used everywhere unless overridden. DEBUG defaults to True.
  • config.settings.prod — imports everything from base, then forces DEBUG=False and enables the security hardening (SSL redirect, secure cookies, HSTS scaffolding, etc.). Select it in production via DJANGO_SETTINGS_MODULE=config.settings.prod.

Production

Docker Compose runs a production-ready stack — Postgres, Redis, a gunicorn web server, and a Celery worker (with beat). The web and Celery services share one image built from Dockerfile (a multi-stage build that compiles the front-end assets with Vite, then installs the production Python dependencies) and run with DJANGO_SETTINGS_MODULE=config.settings.prod.

Requirements: Docker and Docker Compose.

1. Configure secrets

make setup-env-prod    # copies .env.prod.example -> .env.prod (git-ignored)

Edit .env.prod and set real values — at minimum SECRET_KEY, ALLOWED_HOSTS, POSTGRES_PASSWORD / DATABASE_URL, and REDIS_URL. DEBUG=False is required (see the notes in the file). For real email, configure EMAIL_BACKEND and its credentials (e.g. Mailgun via Anymail).

2. Build and run

make prod-build                    # build the production image
make prod-start                    # start the stack (foreground)
make prod-start-bg                 # start the stack (background)
make prod-stop                     # stop the stack
make prod-restart                  # stop + start
make prod-ssh                      # shell into the running web container
make prod-manage ARGS='migrate'    # run a manage.py command in the web container

On startup the web service applies migrations and runs collectstatic automatically, then serves the app with gunicorn on port 8000. Static files are served directly by the app via WhiteNoise — no separate web server is required (put a TLS-terminating reverse proxy in front for HTTPS; the production settings honour the X-Forwarded-Proto header).

What the stack contains

docker-compose.yml defines four services:

Service Image / build Role
db postgres:17 Postgres database (persisted in the postgres_data volume)
redis redis:7 Cache + Celery broker (persisted in redis_data)
web Dockerfile Django app under gunicorn on port 8000 (runs migrate + collectstatic on boot)
celery Dockerfile Celery worker + beat

Uploaded media persists in the media_files volume. MY_UID / MY_GID (in the Makefile) set the container user/group so files created in mounted volumes belong to your host user rather than root. The defaults (1000) suit most setups.

Before going live

Validate the deployment settings:

make prod-manage ARGS='check --deploy'

Consider also enabling HSTS (see the commented block in config/settings/prod.py) once you're confident HTTPS works, and scaling GUNICORN_WORKERS in .env.prod to (2 × CPU cores) + 1.


Built with SaaS Pegasus, the Django SaaS boilerplate.

About

Django Starter Template

Topics

Resources

License

Contributing

Stars

16 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors