Created
November 9, 2025 05:37
-
-
Save yeiichi/54e57c07553cc9a9324ba3fcfc733e24 to your computer and use it in GitHub Desktop.
Automates the creation of a reusable Django + Docker Compose template kit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # ============================================================================== | |
| # make_django_docker_template_kit.sh | |
| # ------------------------------------------------------------------------------ | |
| # PURPOSE: | |
| # Automates the creation of a reusable **Django + Docker Compose** template kit. | |
| # Generates all directories and placeholder files needed to start a new | |
| # containerized Django project, including Docker, Compose, Makefile, and docs. | |
| # | |
| # USAGE: | |
| # bash make_django_docker_template_kit.sh [TARGET_DIR] [PROJECT_SLUG] | |
| # | |
| # TARGET_DIR Directory to create (default: django_project_dir) | |
| # PROJECT_SLUG Django project package name (default: project_name) | |
| # | |
| # EXAMPLES: | |
| # bash make_django_docker_template_kit.sh | |
| # bash make_django_docker_template_kit.sh ./django_project_dir project_name | |
| # | |
| # OUTPUT: | |
| # The script creates a full template layout including: | |
| # - Dockerfile, docker-compose.yml, .env.example | |
| # - Django project skeleton (settings, urls, wsgi, asgi) | |
| # - Postgres init scripts, Makefile helper, and docs/ | |
| # | |
| # NOTES: | |
| # • Safe to rerun — overwrites files but won’t delete existing directories. | |
| # • Intended for local development setup, not production deployment. | |
| # • Review generated Makefile and Docker configuration before first use. | |
| # | |
| # AUTHOR: Eiichi YAMAMOTO | |
| # LICENSE: MIT License | |
| # VERSION: 2025.11 | |
| # URL: https://yeiichi.com | |
| # ============================================================================== | |
| set -euo pipefail | |
| # Display help information | |
| if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then | |
| cat <<'HELP' | |
| Usage: ./make_django_docker_template_kit.sh [TARGET_DIR] [PROJECT_SLUG] | |
| Creates a complete Django + Docker Compose template kit with all necessary | |
| configuration files, directory structure, and development helpers. | |
| Arguments: | |
| TARGET_DIR Directory to create the template in | |
| (default: django-docker-template-kit) | |
| PROJECT_SLUG Django project package name | |
| (default: project_name) | |
| Options: | |
| --help, -h Show this help message and exit | |
| Examples: | |
| # Create template with default settings | |
| ./make_django_docker_template_kit.sh | |
| # Create template in custom directory | |
| ./make_django_docker_template_kit.sh my_project | |
| # Create template with custom directory and project name | |
| ./make_django_docker_template_kit.sh my_project my_app | |
| Generated Structure: | |
| TARGET_DIR/ | |
| ├── README.md Project documentation | |
| ├── .env.example Environment variables template | |
| ├── .gitignore Git ignore rules | |
| ├── .dockerignore Docker ignore rules | |
| ├── docker-compose.yaml Docker Compose configuration | |
| ├── Dockerfile Django container definition | |
| ├── Makefile.django-docker-helper Development command shortcuts | |
| ├── manage.py Django management script | |
| ├── apps/ Django apps directory | |
| │ └── example_app/ | |
| ├── PROJECT_SLUG/ Django project package | |
| │ ├── __init__.py | |
| │ ├── settings.py | |
| │ ├── urls.py | |
| │ ├── asgi.py | |
| │ └── wsgi.py | |
| ├── docker/ Docker-related files | |
| │ ├── django/ | |
| │ │ └── entrypoint.sh | |
| │ └── postgres/ | |
| │ └── init/ | |
| │ ├── 01_extensions.sql | |
| │ └── 02_dev_schema.sql | |
| └── docs/ Documentation | |
| ├── template_overview.md | |
| ├── compose_reference.md | |
| └── makefile_reference.md | |
| After Creation: | |
| 1. cd TARGET_DIR | |
| 2. cp .env.example .env | |
| 3. docker compose up -d (or: make -f Makefile.django-docker-helper up) | |
| 4. docker compose exec web python manage.py migrate | |
| 5. docker compose exec web python manage.py createsuperuser | |
| For more information, visit: https://yeiichi.com | |
| HELP | |
| exit 0 | |
| fi | |
| TARGET_DIR="${1:-django-docker-template-kit}" | |
| PROJECT_SLUG="${2:-project_name}" | |
| echo "Creating template kit at: ${TARGET_DIR} (project slug: ${PROJECT_SLUG})" | |
| mkdir -p "${TARGET_DIR}"/{apps/example_app,"${PROJECT_SLUG}",docker/postgres/init,docker/django,docs} | |
| # README.md | |
| cat > "${TARGET_DIR}/README.md" <<'EOF' | |
| # 🧩 Django + Docker Template Kit | |
| A lightweight, production-ready **development environment** for Django projects using **Docker Compose** and **PostgreSQL**. | |
| Includes a Makefile helper utility for one-line developer commands. | |
| ## Quickstart | |
| cp .env.example .env | |
| docker compose up -d | |
| # or: make up | |
| See `docs/` for detailed references. | |
| EOF | |
| # .env.example | |
| cat > "${TARGET_DIR}/.env.example" <<'EOF' | |
| DJANGO_DEBUG=1 | |
| SECRET_KEY=change-me-in-dev | |
| POSTGRES_DB=appdb | |
| POSTGRES_USER=appuser | |
| POSTGRES_PASSWORD=appsecret | |
| DATABASE_URL=postgres://appuser:appsecret@db:5432/appdb | |
| EOF | |
| # .gitignore | |
| cat > "${TARGET_DIR}/.gitignore" <<'EOF' | |
| # Python | |
| __pycache__/ | |
| *.py[cod] | |
| *$py.class | |
| *.so | |
| .Python | |
| build/ | |
| develop-eggs/ | |
| dist/ | |
| downloads/ | |
| eggs/ | |
| .eggs/ | |
| lib/ | |
| lib64/ | |
| parts/ | |
| sdist/ | |
| var/ | |
| wheels/ | |
| *.egg-info/ | |
| .installed.cfg | |
| *.egg | |
| MANIFEST | |
| # Virtual environments | |
| venv/ | |
| env/ | |
| ENV/ | |
| env.bak/ | |
| venv.bak/ | |
| # Django | |
| *.log | |
| local_settings.py | |
| db.sqlite3 | |
| db.sqlite3-journal | |
| /static/ | |
| /media/ | |
| # Environment variables | |
| .env | |
| .env.local | |
| # IDE | |
| .vscode/ | |
| .idea/ | |
| *.swp | |
| *.swo | |
| *~ | |
| .DS_Store | |
| # Docker | |
| docker-compose.override.yml | |
| # Testing | |
| .pytest_cache/ | |
| .coverage | |
| htmlcov/ | |
| .tox/ | |
| # Secrets | |
| *.pem | |
| *.key | |
| secrets/ | |
| EOF | |
| # .dockerignore | |
| cat > "${TARGET_DIR}/.dockerignore" <<'EOF' | |
| # Version control | |
| .git | |
| .gitignore | |
| .gitattributes | |
| # Python | |
| __pycache__ | |
| *.pyc | |
| *.pyo | |
| *.pyd | |
| .Python | |
| *.so | |
| *.egg | |
| *.egg-info | |
| dist | |
| build | |
| # Virtual environments | |
| venv | |
| env | |
| ENV | |
| # Django | |
| *.log | |
| db.sqlite3 | |
| db.sqlite3-journal | |
| /static/ | |
| /media/ | |
| # Environment | |
| .env | |
| .env.* | |
| !.env.example | |
| # IDE | |
| .vscode | |
| .idea | |
| *.swp | |
| *.swo | |
| *~ | |
| .DS_Store | |
| # Docker | |
| Dockerfile* | |
| docker-compose*.yml | |
| .dockerignore | |
| # Documentation | |
| README.md | |
| docs/ | |
| *.md | |
| # Testing | |
| .pytest_cache | |
| .coverage | |
| htmlcov | |
| .tox | |
| # CI/CD | |
| .github | |
| .gitlab-ci.yml | |
| .travis.yml | |
| # Misc | |
| *.md | |
| LICENSE | |
| Makefile* | |
| EOF | |
| # Makefile stub | |
| cat > "${TARGET_DIR}/Makefile.django-docker-helper" <<'EOF' | |
| # language: Makefile | |
| DC ?= docker compose | |
| WEB ?= web | |
| DB ?= db | |
| .DEFAULT_GOAL := help | |
| help: | |
| @echo "Targets: up, down, reset, migrate, superuser, logs" | |
| up: ; $(DC) up -d | |
| down: ; $(DC) down | |
| reset: ; $(DC) down -v --remove-orphans | |
| migrate: ; $(DC) exec $(WEB) python manage.py migrate | |
| superuser: ; $(DC) exec $(WEB) python manage.py createsuperuser | |
| logs: ; $(DC) logs -f --tail=200 | |
| EOF | |
| # docker-compose.yaml | |
| cat > "${TARGET_DIR}/docker-compose.yaml" <<EOF | |
| services: | |
| web: | |
| build: . | |
| container_name: \${COMPOSE_PROJECT_NAME:-django_app}_web | |
| entrypoint: ["/app/docker/django/entrypoint.sh"] | |
| command: python manage.py runserver 0.0.0.0:8000 | |
| env_file: .env | |
| environment: | |
| DATABASE_URL: \${DATABASE_URL:-postgres://appuser:appsecret@db:5432/appdb} | |
| DJANGO_DEBUG: \${DJANGO_DEBUG:-1} | |
| volumes: | |
| - .:/app:cached | |
| - media:/app/media | |
| - static:/app/static | |
| ports: | |
| - "8000:8000" | |
| depends_on: | |
| db: | |
| condition: service_healthy | |
| db: | |
| image: postgres:16-alpine | |
| container_name: \${COMPOSE_PROJECT_NAME:-django_app}_db | |
| environment: | |
| POSTGRES_DB: \${POSTGRES_DB:-appdb} | |
| POSTGRES_USER: \${POSTGRES_USER:-appuser} | |
| POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-appsecret} | |
| POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C" | |
| healthcheck: | |
| test: ["CMD-SHELL", "pg_isready -U \${POSTGRES_USER:-appuser} -d \${POSTGRES_DB:-appdb}"] | |
| interval: 5s | |
| timeout: 3s | |
| retries: 20 | |
| ports: | |
| - "5432:5432" | |
| volumes: | |
| - pgdata:/var/lib/postgresql/data | |
| - ./docker/postgres/init:/docker-entrypoint-initdb.d:ro | |
| volumes: | |
| pgdata: | |
| media: | |
| static: | |
| EOF | |
| # Dockerfile | |
| cat > "${TARGET_DIR}/Dockerfile" <<'EOF' | |
| FROM python:3.12-slim | |
| WORKDIR /app | |
| ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1 | |
| RUN apt-get update && apt-get install -y --no-install-recommends build-essential curl libpq-dev gettext && rm -rf /var/lib/apt/lists/* | |
| COPY . /app | |
| RUN pip install --upgrade pip && pip install django psycopg2-binary dj-database-url | |
| EXPOSE 8000 | |
| CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] | |
| EOF | |
| # Example app | |
| cat > "${TARGET_DIR}/apps/example_app/__init__.py" <<'EOF' | |
| # Example Django app placeholder | |
| EOF | |
| # manage.py | |
| cat > "${TARGET_DIR}/manage.py" <<EOF | |
| #!/usr/bin/env python | |
| import os, sys | |
| def main(): | |
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', '${PROJECT_SLUG}.settings') | |
| from django.core.management import execute_from_command_line | |
| execute_from_command_line(sys.argv) | |
| if __name__ == '__main__': | |
| main() | |
| EOF | |
| # Django project files | |
| cat > "${TARGET_DIR}/${PROJECT_SLUG}/__init__.py" <<'EOF' | |
| # Django project package | |
| EOF | |
| cat > "${TARGET_DIR}/${PROJECT_SLUG}/settings.py" <<EOF | |
| import os | |
| from pathlib import Path | |
| BASE_DIR = Path(__file__).resolve().parent.parent | |
| SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key") | |
| DEBUG = os.getenv("DJANGO_DEBUG", "1") == "1" | |
| ALLOWED_HOSTS = ["*"] | |
| INSTALLED_APPS = ["django.contrib.admin","django.contrib.auth","django.contrib.contenttypes","django.contrib.sessions","django.contrib.messages","django.contrib.staticfiles"] | |
| MIDDLEWARE = ["django.middleware.security.SecurityMiddleware","django.contrib.sessions.middleware.SessionMiddleware","django.middleware.common.CommonMiddleware","django.middleware.csrf.CsrfViewMiddleware","django.contrib.auth.middleware.AuthenticationMiddleware","django.contrib.messages.middleware.MessageMiddleware","django.middleware.clickjacking.XFrameOptionsMiddleware"] | |
| ROOT_URLCONF = "${PROJECT_SLUG}.urls" | |
| TEMPLATES = [{"BACKEND":"django.template.backends.django.DjangoTemplates","DIRS":[BASE_DIR / "templates"],"APP_DIRS":True,"OPTIONS":{"context_processors":["django.template.context_processors.debug","django.template.context_processors.request","django.contrib.auth.context_processors.auth","django.contrib.messages.context_processors.messages"]}}] | |
| WSGI_APPLICATION = "${PROJECT_SLUG}.wsgi.application" | |
| ASGI_APPLICATION = "${PROJECT_SLUG}.asgi.application" | |
| # Database configuration | |
| try: | |
| import dj_database_url | |
| DATABASES = { | |
| "default": dj_database_url.config( | |
| default="sqlite:///" + str(BASE_DIR / "db.sqlite3"), | |
| conn_max_age=600 | |
| ) | |
| } | |
| except ImportError: | |
| DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": BASE_DIR / "db.sqlite3"}} | |
| STATIC_URL = "static/" | |
| STATIC_ROOT = BASE_DIR / "static" | |
| MEDIA_URL = "media/" | |
| MEDIA_ROOT = BASE_DIR / "media" | |
| DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" | |
| EOF | |
| cat > "${TARGET_DIR}/${PROJECT_SLUG}/urls.py" <<EOF | |
| from django.contrib import admin | |
| from django.urls import path | |
| urlpatterns = [path("admin/", admin.site.urls)] | |
| EOF | |
| cat > "${TARGET_DIR}/${PROJECT_SLUG}/asgi.py" <<EOF | |
| import os | |
| from django.core.asgi import get_asgi_application | |
| os.environ.setdefault("DJANGO_SETTINGS_MODULE", "${PROJECT_SLUG}.settings") | |
| application = get_asgi_application() | |
| EOF | |
| cat > "${TARGET_DIR}/${PROJECT_SLUG}/wsgi.py" <<EOF | |
| import os | |
| from django.core.wsgi import get_wsgi_application | |
| os.environ.setdefault("DJANGO_SETTINGS_MODULE", "${PROJECT_SLUG}.settings") | |
| application = get_wsgi_application() | |
| EOF | |
| # Postgres init SQL | |
| cat > "${TARGET_DIR}/docker/postgres/init/01_extensions.sql" <<'EOF' | |
| CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; | |
| CREATE EXTENSION IF NOT EXISTS pgcrypto; | |
| CREATE EXTENSION IF NOT EXISTS citext; | |
| EOF | |
| cat > "${TARGET_DIR}/docker/postgres/init/02_dev_schema.sql" <<'EOF' | |
| DO $$ | |
| BEGIN | |
| IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'dev') THEN | |
| EXECUTE 'CREATE SCHEMA dev AUTHORIZATION ' || quote_ident(current_user); | |
| END IF; | |
| END$$; | |
| ALTER DATABASE appdb SET search_path = dev, public; | |
| GRANT ALL ON SCHEMA dev TO PUBLIC; | |
| EOF | |
| # Entrypoint | |
| cat > "${TARGET_DIR}/docker/django/entrypoint.sh" <<'EOF' | |
| #!/usr/bin/env bash | |
| set -e | |
| if [ "${RUN_MIGRATIONS:-1}" = "1" ]; then | |
| python manage.py migrate --noinput || true | |
| fi | |
| exec "$@" | |
| EOF | |
| chmod +x "${TARGET_DIR}/docker/django/entrypoint.sh" | |
| # Docs | |
| cat > "${TARGET_DIR}/docs/template_overview.md" <<'EOF' | |
| # Template Overview | |
| Dockerized Django + PostgreSQL 16 environment with health checks and init SQL. | |
| EOF | |
| cat > "${TARGET_DIR}/docs/compose_reference.md" <<'EOF' | |
| # Compose Reference | |
| - web: Django app, port 8000 | |
| - db: PostgreSQL 16 with init scripts | |
| EOF | |
| cat > "${TARGET_DIR}/docs/makefile_reference.md" <<'EOF' | |
| # Makefile Reference | |
| Common targets: up, down, reset, migrate, superuser, logs. | |
| EOF | |
| echo "✅ Template created at: ${TARGET_DIR}" | |
| echo "Next steps:" | |
| echo " cd ${TARGET_DIR}" | |
| echo " cp .env.example .env" | |
| echo " docker compose up -d # or: make up" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment