Django開発環境メモ（Dockerバージョン）

## プロジェクトディレクトリ作成
```bash
mkdir django-project && cd django-project
```

## ディレクトリ構成
```bash
❯ tree
.
├── app
│   ├── Dockerfile
│   ├── manage.py
│   ├── config
│   │   ├── asgi.py
│   │   ├── __init__.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   ├── Pipfile
│   ├── Pipfile.lock
│   └── requirements.txt
├── compose.yml
└── db
```
## .gitignore 準備
こちらのお世話になる。
https://djangowaves.com/tips-tricks/gitignore-for-a-django-project/
その上で以下を追加
```
# Pipenv
Pipfile.lock
```

## appディレクトリ作成、移動
```bash
mkdir app && cd app
```

## requirements.txt 作成
```
Django==5.2
psycopg2-binary
```

## Djangoアプリ作成
```bash
# アプリ作成用コンテナ起動
docker container run --rm -it -w "/app" -v "${PWD}":/app  python:3.11-slim /bin/bash

# pip関連実行
python -m pip install --upgrade pip \
    && pip install pipenv \
    && pipenv install -r requirements.txt --skip-lock \
    && pipenv lock

# djangoアプリ作成
pipenv shell
django-admin startproject config .
```

## django開発用Dockerfile
```dockerfile
FROM python:3.11-slim

# 環境変数で .venv をプロジェクト直下に作らせる
ENV PIPENV_VENV_IN_PROJECT=1

# 必要なパッケージやツールをインストール（pipenv と依存関係のインストール）
RUN apt-get update && apt-get install -y sudo \
    && rm -rf /var/lib/apt/lists/* \
    && pip install --upgrade pip \
    && pip install pipenv

# vscode ユーザーの作成（UID=1000はVS Codeと一致させる）
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

RUN groupadd --gid $USER_GID $USERNAME \
    && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
    && echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/$USERNAME \
    && chmod 0440 /etc/sudoers.d/$USERNAME

# 作業ディレクトリの設定
WORKDIR /app

# 必要なファイルをコピー
COPY Pipfile Pipfile.lock /app/

# 権限変更（pipenv installをvscodeユーザーで行うため）
RUN chown -R $USERNAME:$USERNAME /app

# vscodeユーザーに切り替え
USER $USERNAME

# pipenvを使って依存関係をインストール（ロックファイルを基にインストール）
RUN pipenv install --deploy --ignore-pipfile

# アプリケーションの全体をコピー（vscodeユーザーの権限で）
COPY --chown=vscode:vscode . /app/

# ポート明示（任意）
EXPOSE 8000

# コンテナ起動時に実行するコマンドを設定
CMD ["pipenv", "run", "python", "manage.py", "runserver", "0.0.0.0:8000"]
```

## .dockerignore 作成
```
# 仮想環境などビルド不要なもの
.venv/
__pycache__/
*.pyc
*.pyo
*.pyd

# エディタ関連
.vscode/
.idea/

# 本番で不要なファイル
.env
db.sqlite3
*.log
```

## コンテナ起動
```bash
## イメージ作成
 docker image build -t django-app . 

## コンテナ起動
docker container run -p 8000:8000 django-app:latest
```
`http://localhost:8080`にブラウザでアクセスし起動確認

## compose.yml作成
```yaml
services:

  app:
    build: ./app
    container_name: django-app
    command: pipenv run python manage.py runserver 0.0.0.0:8000
    volumes:
     - ./app:/app
     - venv_data:/app/.venv
    ports:
      - "8000:8000"
    environment:
      DEBUG: "${DEBUG}"
      SECRET_KEY: "${SECRET_KEY}"
      ALLOWED_HOSTS: "${ALLOWED_HOSTS}"
      TIME_ZONE: "${TIME_ZONE}"
      POSTGRES_DB: "${POSTGRES_DB}"
      POSTGRES_USER: "${POSTGRES_USER}"
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
      POSTGRES_HOST: "${POSTGRES_HOST}"
      POSTGRES_PORT: "${POSTGRES_PORT}"
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s

  db:
    image: postgres:15-alpine
    container_name: django-db
    volumes:
      - db_data:/var/lib/postgresql/data/
    environment:
      POSTGRES_DB: "${POSTGRES_DB}"
      POSTGRES_USER: "${POSTGRES_USER}"
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER}", "-d", "${POSTGRES_DB}"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 5s

volumes:
  db_data:
  venv_data:
```

## .envファイル作成
```
DEBUG=True
SECRET_KEY=your_very_secret_key_here
ALLOWED_HOSTS=*
TIME_ZONE=Asia/Tokyo

POSTGRES_DB=myproject
POSTGRES_USER=django
POSTGRES_PASSWORD=your_password_here
POSTGRES_HOST=django-db
POSTGRES_PORT=5432
```

## Django settings.py
```python
import os

DEBUG = os.getenv('DEBUG', 'False').lower() == 'true'

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('POSTGRES_DB', 'myproject'),
        'USER': os.environ.get('POSTGRES_USER', 'django'),
        'PASSWORD': os.environ.get('POSTGRES_PASSWORD', 'your_password'),
        'HOST': os.environ.get('POSTGRES_HOST', 'db'),
        'PORT': int(os.environ.get('POSTGRES_PORT', 5432)),
    }
}

TIME_ZONE = os.environ.get('TIME_ZONE', 'UTC')
```

## devcontainer.json
```jsonc
{
  "name": "Django Dev",
  "dockerComposeFile": ["../compose.yml"],
  "service": "app",
  "workspaceFolder": "/app",
  "forwardPorts": [8000],
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance",
        "charliermarsh.ruff",
        "batisteo.vscode-django",
        "oderwat.indent-rainbow",
        "ms-ceintl.vscode-language-pack-ja"
      ],
      "settings": {
        "python.defaultInterpreterPath": "/app/.venv/bin/python",
        "python.languageServer": "Pylance",
        "python.formatting.provider": "none", // Ruff を使うため、他のフォーマッタは無効化
        "python.linting.enabled": true,
        "python.linting.pylintEnabled": false,
        // Ruffをデフォルトのフォーマッタとして設定
        "[python]": {
          "editor.defaultFormatter": "charliermarsh.ruff",
          "editor.formatOnSave": true,
          "editor.codeActionsOnSave": {
            "source.organizeImports": "explicit", // Ruffがisortの機能を含むため、これで対応
            "source.fixAll": "explicit" // RuffのLint修正も保存時に実行
          }
        },
        // 日本語パックを有効にするための設定
        "locale": "ja"
      }
    }
  },
  "remoteUser": "vscode",
  "features": {}
}
```

## pyproject.toml(appディレクトリ直下)
```toml
[tool.ruff]
exclude = [
    "migrations",
    "venv",
    ".venv",
]
line-length = 88
target-version = "py311"

[tool.ruff.lint]
select = [
    "F", # Flake8
    "B", # Black
    "I", # isort
    "E", # error
    "W"  # warning
]
ignore = []
fixable = ["E", "W", "I"]

```