Django en ligne : poser un environnement de développement solide dès le jour 1
Chapo. Fini les “ça marche chez moi”. En basculant votre poste de travail vers un IDE cloud et des conteneurs reproductibles, vous standardisez le dev, sécurisez les secrets et rapprochez enfin le quotidien de la production. Voici l’introduction structurée pour bien démarrer un projet Django en ligne, avec une architecture claire, des settings 12-Factor et des variables d’environnement bien tenues.
Pourquoi développer en ligne change la donne
La diversité des machines et des habitudes fragilise la qualité. Un environnement hébergé (Codespaces, Gitpod, ou tout IDE cloud équivalent) égalise les versions, accélère l’onboarding et simplifie les revues. Résultat : moins de frictions, plus de focus sur le produit, et une parité dev-prod qui limite les surprises au déploiement.
La structure qui tient la route
Un projet lisible commence par une intention : un dossier src/ qui contient le code applicatif, un paquet config/ pour les settings/ASGI/WSGI/urls, un répertoire apps/ pour vos applications métier, et des espaces dédiés aux templates, static et tests. Cette topologie évite les collisions d’import, clarifie les responsabilités et prépare la CI/CD.
Les settings en mode 12-Factor
Le cœur des erreurs de déploiement se niche dans les réglages. On isole l’invariant dans base.py, et l’on superpose dev.py et prod.py pour activer le confort de dev ou les garde-fous de sécurité. Clé secrète, base de données, cache, email, niveau de logs : tout provient de l’environnement, jamais du code.
Variables d’environnement : l’hygiène qui paie
Les env vars sont votre contrat avec l’infrastructure. Un .env.example documente les clés attendues (sans secrets), tandis que les secrets réels vivent côté coffre (IDE, plateforme, CI). On y place DJANGO_SECRET_KEY, DATABASE_URL, ALLOWED_HOSTS, CSRF_TRUSTED_ORIGINS, DJANGO_LOG_LEVEL, etc. Le code lit l’environnement, la plateforme injecte les valeurs.
Lancer le projet sans bricolage
Sur un IDE cloud, la routine est simple : installer les dépendances, exporter DJANGO_SETTINGS_MODULE=config.settings.dev, exécuter migrate puis runserver 0.0.0.0:8000. On ajoute l’URL de prévisualisation aux hôtes autorisés et aux origines CSRF, et l’équipe voit la même chose, au même endroit, au même moment.
Données, fichiers, logs : stateless par design
Le conteneur n’est pas un disque. Les fichiers utilisateurs (MEDIA) partent vers un stockage persistant ; les statiques sont collectés en prod (Whitenoise/CDN) ; les sessions et caches reposent sur des services gérés (DB/Redis). Les logs sortent en flux vers stdout/stderr pour être agrégés proprement. Ce choix “stateless” assure l’élasticité sans dettes cachées.
Sécurité pragmatique, sans sur-ingénierie
HTTPS activé, cookies sécurisés, HSTS, URL d’admin discrète, 2FA, CORS/CSRF en liste blanche, et revue systématique des secrets. En ligne comme en prod, la surface d’attaque diminue quand la configuration est stricte et centralisée dans l’environnement.
Ce que vous gagnez concrètement
Onboarding en minutes, exécution cohérente entre dev et prod, diagnostics facilités par des logs normalisés, et une architecture prête pour web/worker/scheduler. Vous codez, testez, déployez—sans “magie” locale ni écarts non maîtrisés.
Guide pas-à-pas : démarrer, structurer, sécuriser
Un environnement Django en ligne devient un multiplicateur de vitesse quand la structure, les settings 12-Factor et les env vars sont cadrés dès le départ. Vous gagnez en reproductibilité, en sécurité et en sérénité de déploiement.
Objectif. Passer de l’intention à l’exécution : un projet Django en ligne prêt à coder, tester et déployer, avec une structure nette, des settings 12-Factor, et des env vars maîtrisées.
1) Initialiser le dépôt proprement
Rituel de base
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install --upgrade pip
pip install django dj-database-url python-dotenv
mkdir -p src && cd src
django-admin startproject config .
django-admin startapp core apps/core
Arborescence de référence
project-root/
├─ .gitignore
├─ .env.example
├─ pyproject.toml # ou requirements.txt / constraints.txt
├─ devcontainer.json # optionnel (IDE cloud / VS Code)
├─ docker/ # optionnel (Dockerfiles/scripts)
├─ manage.py
├─ src/
│ ├─ config/
│ │ ├─ asgi.py
│ │ ├─ wsgi.py
│ │ └─ settings/
│ │ ├─ base.py
│ │ ├─ dev.py
│ │ └─ prod.py
│ ├─ apps/core/
│ ├─ templates/
│ └─ static/
└─ tests/
Pourquoi src/ ? Pour éviter les collisions d’import et séparer outils/CI du code métier.
2) Settings factorisés et lisibles
base.py : l’invariant
# src/config/settings/base.py
from pathlib import Path
import os
import dj_database_url
BASE_DIR = Path(__file__).resolve().parents[2] # .../project-root
def env(key, default=None, cast=str):
val = os.getenv(key, default)
return cast(val) if (val is not None and cast and cast is not str) else val
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "change-me") # jamais en prod
DEBUG = os.getenv("DJANGO_DEBUG", "0") == "1"
ALLOWED_HOSTS = os.getenv("DJANGO_ALLOWED_HOSTS", "").split(",") if os.getenv("DJANGO_ALLOWED_HOSTS") else []
CSRF_TRUSTED_ORIGINS = os.getenv("DJANGO_CSRF_TRUSTED_ORIGINS", "").split(",") if os.getenv("DJANGO_CSRF_TRUSTED_ORIGINS") else []
LANGUAGE_CODE = os.getenv("DJANGO_LANGUAGE_CODE", "fr-fr")
TIME_ZONE = os.getenv("DJANGO_TIME_ZONE", "Europe/Paris")
USE_I18N = True
USE_TZ = True
INSTALLED_APPS = [
"django.contrib.admin","django.contrib.auth","django.contrib.contenttypes",
"django.contrib.sessions","django.contrib.messages","django.contrib.staticfiles",
"apps.core",
]
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 = "config.urls"
WSGI_APPLICATION = "config.wsgi.application"
ASGI_APPLICATION = "config.asgi.application"
DATABASES = {"default": dj_database_url.parse(os.getenv("DATABASE_URL","sqlite:///db.sqlite3"))}
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
LOGGING = {
"version": 1, "disable_existing_loggers": False,
"handlers": {"console": {"class": "logging.StreamHandler"}},
"root": {"handlers": ["console"], "level": os.getenv("DJANGO_LOG_LEVEL","INFO")},
}
Overlays
# src/config/settings/dev.py
from .base import *
DEBUG = True
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")
# src/config/settings/prod.py
from .base import *
SECURE_SSL_REDIRECT = os.getenv("DJANGO_SSL_REDIRECT","1") == "1"
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = int(os.getenv("DJANGO_HSTS_SECONDS","0"))
Sélection à l’exécution
export DJANGO_SETTINGS_MODULE=config.settings.dev # ou prod
3) Variables d’environnement : votre contrat d’infra
.env.example (versionné, sans secrets)
DJANGO_SECRET_KEY=changeme
DJANGO_DEBUG=1
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost:8000
DJANGO_LOG_LEVEL=INFO
DJANGO_LANGUAGE_CODE=fr-fr
DJANGO_TIME_ZONE=Europe/Paris
DATABASE_URL=sqlite:///db.sqlite3
# DATABASE_URL=postgres://USER:PASS@HOST:5432/DBNAME
DJANGO_SSL_REDIRECT=0
DJANGO_HSTS_SECONDS=0
Bonnes pratiques
- Commiter
.env.example, ignorer.env. - Stocker les secrets dans le coffre de votre IDE cloud/CI.
- Documenter chaque clé attendue (commentaire ligne-à-ligne).
4) Démarrer en IDE cloud (prévisualisation incluse)
Routine
pip install -r requirements.txt # ou: poetry install
export DJANGO_SETTINGS_MODULE=config.settings.dev
python manage.py migrate
python manage.py runserver 0.0.0.0:8000
Détails qui évitent les pièges
- Ajouter l’URL dynamique de preview à ALLOWED_HOSTS et CSRF_TRUSTED_ORIGINS.
- Exposer le port attendu par la plateforme (souvent 8000).
- Préférer Postgres à SQLite si vous partagez le workspace à plusieurs.
5) Qualité de code et garde-fous automatiques
pyproject.toml minimal
[tool.black]
line-length = 100[tool.isort]
profile = « black »
[tool.ruff]
line-length = 100 select = [« E », »F », »I », »UP »]
pre-commit (extrait)
repos:
- repo: https://github.com/psf/black
rev: 24.8.0
hooks: [{id: black}]
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.6.9
hooks: [{id: ruff}]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: detect-private-key
Tests
pip install pytest pytest-django coverage
pytest -q
6) Process model : web, worker, scheduler
Procfile (exemple Heroku-like)
web: gunicorn config.wsgi:application --bind 0.0.0.0:$PORT
worker: celery -A config worker -l info
beat: celery -A config beat -l info
Réglez les env vars séparément (ex. CELERY_BROKER_URL, REDIS_URL) ; ne mélangez pas les commandes dans un seul process.
7) Docker/Compose (optionnel mais puissant)
docker-compose.yml de dev
services:
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
env_file: .env
environment:
- DJANGO_SETTINGS_MODULE=config.settings.dev
volumes:
- .:/app
ports: ["8000:8000"]
depends_on: [db, redis]
db:
image: postgres:16
environment:
POSTGRES_USER: django
POSTGRES_PASSWORD: django
POSTGRES_DB: django
ports: ["5432:5432"]
redis:
image: redis:7
ports: ["6379:6379"]
Ajustement côté settingsDATABASE_URL=postgres://django:django@db:5432/django et un cache/broker pointés sur redis://redis:6379/0.
8) Fichiers statiques, médias, logs : le “stateless” appliqué
- Statiques : Whitenoise en prod ou CDN ;
collectstaticen phase build. - Médias : stockage persistant (S3/GCS/NFS). Jamais dans le conteneur.
- Logs : stdout/stderr, niveau piloté par
DJANGO_LOG_LEVEL, agrégation côté plateforme.
9) Sécurité pragmatique
- HTTPS forcé (
DJANGO_SSL_REDIRECT=1), HSTS en prod. - Cookies Secure, CSRF/CORS en liste blanche uniquement.
- URL d’admin personnalisée, 2FA, rotation des clés.
- Revue automatique de secrets (hook pre-commit + scan en CI).
10) Check-list d’onboarding
- Cloner le repo et ouvrir le workspace en ligne.
- Installer deps (
pipoupoetry). - Renseigner secrets côté coffre (SECRET_KEY, DATABASE_URL…).
- Exporter
DJANGO_SETTINGS_MODULE=config.settings.dev. migratepuisrunserver 0.0.0.0:8000.- Ajouter l’URL de preview aux hosts/CSRF.
- Lancer tests (
pytest) et vérifier lint.
11) Erreurs et correctifs rapides)
- 403 CSRF en preview → manquerait l’URL dans
CSRF_TRUSTED_ORIGINS. - DisallowedHost → ajouter l’hôte de preview à
ALLOWED_HOSTS. - SQLite lock en pair-programming → passer à Postgres.
- Fichiers médias perdus → basculer vers un stockage persistant.
- Logs muets → vérifier
DJANGO_LOG_LEVELet l’agrégateur de la plateforme.
12) Feuille de route minimale CI/CD
- Étape Build : installer deps, lancer
collectstatic, empaqueter l’image. - Étape Test :
pytest,ruff,black --check. - Étape Release : injection des env vars et migration (
manage.py migrate). - Étape Run : process web/worker/beat distincts, santé surveillée.









