🐍 Django – Installation & Déploiement complet
Guide complet IDEO‑Lab pour partir de zéro et aller jusqu'au déploiement Nginx/Gunicorn/WAMP.
Vue d'ensemble Django
Django, MTV, sécurité intégrée, admin auto.
MTV Sécu ORMInstallation (Linux/Win)
Python, venv, pip, installation Django.
venv pip Win/LinuxCréer le projet
startproject, startapp, arborescence.
startproject appsParamétrer settings.py
DEBUG, ALLOWED_HOSTS, apps, templates.
settings.py ALLOWED_HOSTS1.4B Générateur settings.py
Outil interactif : Créez votre configuration Django idéale en quelques clics.
Interactif GeneratorApps de base Django
accounts, blog, core, dashboard.
accounts admin reusableRoutage & URLs
urls.py projet + urls.py app.
path include2.4 Health Check & Analyzer
Outil expérimental : Cross-reference URLs/Templates pour détecter les "NoReverseMatch" avant le crash.
Analysis Anti-CrashTemplates & static
Organisation /templates et /static.
extends static base.htmlModèles & ORM
models.Model, migrations, requêtes ORM.
models migrate querysetConnexion BDD
SQLite, PostgreSQL, MariaDB.
sqlite pgsql mariadbAdmin Django
Activation et customisation rapide.
admin ModelAdminFormulaires
Django forms, ModelForm, validation.
forms ModelFormAuth & Users
Login, logout, permissions.
login permissionAPI (DRF option)
Base DRF, serializers, viewsets.
DRF APIRunserver & gestion
python manage.py runserver.
runserver manage.pyGunicorn (Linux)
Serveur WSGI pour la prod.
gunicorn systemdNginx reverse proxy
Proxy, static, media, SSL.
nginx proxy_passWAMP / Windows
Django sous Windows, Apache.
wamp mod_wsgiSécurité & .env
DEBUG, secrets, CSRF.
.env secretsOptimisation
Collectstatic, cache, logs.
collectstatic logsDéploiement complet
Arbo serveur, services, backups.
deploy prodCheat‑sheet Django
Commandes fréquentes.
cheat cmdDjango en bref
Django est un framework web Python de haut niveau, conçu pour les développeurs perfectionnistes ayant des délais serrés. Il suit le principe DRY (Don't Repeat Yourself) et privilégie la configuration explicite.
- Date de création : 2005 (Lawrence Journal-World).
- Langage : Python (3.10+ recommandé pour Django 5).
- Type : Monolithique (tout inclus), mais modulaire via les "Apps".
Architecture MTV vs MVC
Django utilise une variation du MVC appelée MTV.
MVC Classique | Django MTV
------------------------------------------
Model (Données) | Model (models.py)
View (Affichage) | Template (html)
Controller (Logique) | View (views.py)
Note: Le "Controller" est en réalité géré par le framework lui-même (URL Dispatching).
Diagramme de flux MTV
Navigateur (Client)
│
▼ (Requête HTTP)
[ URL Dispatcher (urls.py) ] ──▶ Choisit la bonne vue
│
▼
[ View (views.py) ] ◀──┐
│ │ │ (ORM: Python <-> SQL)
│ │ logique ▼
│ └────────── [ Model (models.py) ] ◄───► [ Base de Données ]
│
▼ (Context Data)
[ Template (HTML) ] ──▶ Génère la page finale
│
▼
Réponse HTTP (HTML, JSON, File...)
Le chemin d'une requête
Comprendre le cycle de vie est crucial pour le débogage et la performance.
- WSGI/ASGI : Le serveur web (Nginx/Gunicorn) passe la main à Django.
- Middleware (Entrée) : La requête traverse les couches de sécurité, session, CSRF.
- URL Resolution : Django cherche une correspondance dans
urlpatterns. - View Processing : La fonction ou classe est exécutée.
- Middleware (Sortie) : La réponse traverse les couches en sens inverse.
Le concept d'"Oignon" (Middleware)
Requête Entrante ⬇️ ⬆️ Réponse Sortante
┌──────────────────────────────────────────┐
│ SecurityMiddleware │
│ ┌────────────────────────────────────┐ │
│ │ SessionMiddleware │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ CommonMiddleware │ │ │
│ │ │ ┌────────────────────────┐ │ │ │
│ │ │ │ CSRFViewMiddleware │ │ │ │
│ │ │ │ ┌──────────────────┐ │ │ │ │
│ │ │ │ │ AuthMiddleware │ │ │ │ │
│ │ │ │ │ YOUR VIEW │ │ │ │ │
│ │ │ │ └──────────────────┘ │ │ │ │
│ │ │ └────────────────────────┘ │ │ │
│ │ └──────────────────────────────┘ │ │
│ └────────────────────────────────────┘ │
└──────────────────────────────────────────┘
Pourquoi dit-on "Batteries Included" ?
Contrairement aux micro-frameworks (Flask, FastAPI), Django fournit nativement les outils pour 90% des besoins web standards.
Sécurité par défaut
Django protège proactivement contre :
- SQL Injection : Via l'utilisation des querysets paramétrés de l'ORM.
- XSS (Cross Site Scripting) : Les templates échappent automatiquement les variables dangereuses.
- CSRF (Cross Site Request Forgery) : Tokens obligatoires pour les formulaires POST.
- Clickjacking : Protection via headers X-Frame-Options.
Arborescence Typique
my_project/ # Racine (Repo Git)
│
├── manage.py # CLI (Command Line Interface)
├── requirements.txt # Dépendances
├── .env # Secrets (ne pas commiter)
│
├── my_project/ # Dossier de configuration
│ ├── __init__.py
│ ├── settings.py # Config globale
│ ├── urls.py # Routes principales
│ ├── wsgi.py # Point d'entrée serveur Sync
│ └── asgi.py # Point d'entrée serveur Async
│
└── core/ # Une "App" (Module métier)
├── migrations/ # Versionning BDD
├── admin.py # Config Admin
├── apps.py # Config App
├── models.py # Structure des données
├── tests.py # Tests unitaires
├── urls.py # Routes de l'app
└── views.py # Logique des vues
Commandes Essentielles
Le fichier manage.py est votre baguette magique.
| Commande | Description |
|---|---|
runserver | Lance le serveur de dev (hot-reload). |
makemigrations | Détecte les changements dans models.py. |
migrate | Applique les changements SQL en base. |
createsuperuser | Crée un admin avec accès complet. |
shell | Ouvre un shell Python avec le contexte Django chargé. |
Architecture conceptuelle d’un projet Django
Vue “macro” d’un projet : du noyau framework jusqu’aux briques métier. Idéal pour expliquer Django en 1 slide.
Django Project
├── Core & Settings
│ ├── settings.py (config, I18N, DB, sécurité)
│ ├── urls.py (routing)
│ └── wsgi/asgi.py (interface serveur)
│
├── Apps (briques métiers réutilisables)
│ ├── models.py <= ORM (schéma & logique de données)
│ ├── views.py <= logique métier / contrôleur
│ ├── templates/ <= rendu HTML / UI
│ ├── forms.py <= validation & UX des formulaires
│ └── admin.py <= back-office d’administration
│
├── Cross-Cutting Concerns
│ ├── Middleware (logs, sécurité, perf, monitoring)
│ ├── Auth / Permissions
│ ├── Messages / Sessions
│ └── Cache / Files / Storage
│
└── Intégrations externes
├── Base de données (PostgreSQL, MariaDB…)
├── Cache (Redis, Memcached)
├── File storage (S3, GCS, disque…)
├── Tâches asynchrones (Celery, RQ…)
└── APIs / Services tiers (REST, GraphQL, webhooks…)
Tableau des briques majeures de Django
Les 11 piliers “structurants” du framework, avec leur rôle et quelques mots-clés clés.
| # | Pilier | Rôle principal | Mots-clés / Fichiers |
|---|---|---|---|
| 1 | Core & Settings | Configuration globale du projet et enregistrement des apps. | settings.py, INSTALLED_APPS, MIDDLEWARE, apps.py |
| 2 | Routing (URLs) | Fait le lien entre une URL et une vue Python. | urls.py, path(), re_path(), namespaces |
| 3 | Views & HTTP | Logique métier, orchestration des réponses HTTP. | FBV/CBV, HttpRequest, HttpResponse, mixins |
| 4 | Templates | Rendu HTML (ou texte) côté serveur, avec tags & filtres. | {% extends %}, {% include %}, filters, context processors |
| 5 | ORM & Models | Abstraction SQL, mapping objet <-> base de données. | models.py, QuerySet, managers, migrations |
| 6 | Forms & Validation | Validation déclarative des données et génération de formulaires. | forms.Form, ModelForm, clean(), widgets |
| 7 | Sécurité | Protection CSRF/XSS, gestion des cookies, headers de sécurité. | CSRF, clickjacking, host validation, password hashing |
| 8 | Auth & Permissions | Gestion des utilisateurs, groupes, droits d’accès. | User, Group, permissions, auth backends, decorators |
| 9 | Admin | Back-office auto-généré pour gérer les modèles. | admin.site.register, ModelAdmin, inlines, filters |
| 10 | Static, Media & Storage | Gestion des assets (CSS/JS/images) et des fichiers uploadés. | STATIC_URL, STATIC_ROOT, MEDIA_ROOT, storage backends |
| 11 | I18N, Timezones & Localisation | Multi-langues et gestion des fuseaux horaires. | LANGUAGE_CODE, USE_I18N, USE_TZ, ugettext |
Écosystème Django & briques complémentaires
Django fournit un noyau complet, mais la plupart des projets modernes ajoutent des “super-pouvoirs” via quelques librairies phares.
API & sérialisation
| Addon | Usage principal |
|---|---|
| Django REST Framework | Construire des APIs REST robustes (sérialisation, viewsets, permissions). |
| Graphene-Django | Schema GraphQL au-dessus de l’ORM Django. |
Tâches asynchrones & temps différé
| Celery | Exécution de tâches en arrière-plan (emails, batchs lourds, ETL). |
| Redis / RabbitMQ | Broker & backend de résultats pour Celery. |
Authentification avancée & Social Login
| django-allauth | Gestion complète de l’inscription, login, social auth (Google, GitHub…). |
Debug, perf & tooling
| Django Debug Toolbar | Profiling des requêtes SQL, temps de rendu, contexte templates. |
| Sentry | Monitoring des erreurs et traces en production. |
| django-extensions | Shell plus riche, diagrammes de modèles, commandes de management. |
Vérification de Python
Django est un framework Python. La version requise dépend de la version de Django visée.
- Django 5.x : Python 3.10, 3.11, 3.12+
- Django 4.2 (LTS) : Python 3.8 à 3.12
# Vérifier la version
python --version
# ou sous Linux/Mac
python3 --version
Installation Système
Avant de créer un environnement virtuel, assurez-vous d'avoir Python et pip installés globalement.
Ubuntu / Debian
sudo apt update
sudo apt install python3 python3-venv python3-pip -yWindows
Téléchargez l'installateur sur python.org.
IMPORTANT : Cochez la case Add Python to PATH lors de l'installation.
Pourquoi un environnement virtuel (venv) ?
Le venv est un dossier isolé contenant son propre exécutable Python et ses propres librairies. Il permet d'avoir Django 4 sur le projet A et Django 5 sur le projet B sans conflit.
1. Création
Placez-vous dans le dossier de votre projet.
# Créer le dossier "venv"
python -m venv venv
# ou
python3 -m venv venv
2. Activation
Une fois activé, votre prompt terminal changera (ex: (venv) user@pc...).
| OS | Commande |
|---|---|
| Linux / Mac | source venv/bin/activate |
| Windows (CMD) | venv\Scripts\activate.bat |
| Windows (PowerShell) | venv\Scripts\Activate.ps1 |
Installation Standard
# Mettre à jour pip (bon réflexe)
python -m pip install --upgrade pip
# Installer la dernière version stable
pip install django
# Vérifier l'installation
python -m django --version
Version Spécifique & Requirements
# Installer une version précise (ex: LTS)
pip install django==4.2.7
# Figer les versions pour la prod
pip freeze > requirements.txt
# Installer depuis un fichier
pip install -r requirements.txt
Structure recommandée
mon_projet/
├── venv/ # L'environnement (NE PAS TOUCHER)
├── .gitignore # Ignorer venv/, .env, *.pyc
├── requirements.txt # Liste des paquets
└── src/ # Le code source (optionnel, pour séparer)
Erreur : "Scripts is disabled on this system" (Windows)
PowerShell bloque l'exécution de scripts par défaut.
# Solution : Autoriser l'exécution (Admin)
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Erreur : "pip command not found"
Python n'est pas dans le PATH ou pip n'est pas installé.
# Essayez d'utiliser le module python directement
python -m pip install django
# Au lieu de juste "pip install ..."
Bonnes Pratiques
- DO : Toujours utiliser un venv par projet.
- DO : Ajouter
venv/à votre fichier.gitignoreimmédiatement. - DON'T : Ne jamais renommer ou déplacer le dossier
venvune fois créé (cela casse les chemins absolus internes). Supprimez-le et recréez-le si besoin.
La distinction Projet vs Application
C'est la confusion n°1 des débutants. Django est conçu pour la **réutilisabilité**. Une application ne devrait pas "savoir" à quel projet elle appartient.
C'est votre site web spécifique (ex: mon_e_commerce). Il contient la configuration (BDD, langue, timezones) et le routage global.
C'est une fonctionnalité autonome (ex: forum, blog, auth). Une app bien écrite peut être copiée/collée dans un autre projet et fonctionner immédiatement.
Architecture Multi-Apps (Pattern Standard)
┌───────────────── PROJET: "IDEO_PLATFORM" ─────────────────┐
│ settings.py (Connecteur DB: PostgreSQL) │
│ urls.py (Dispatcher global) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ APP "Core" │ │ APP "Blog" │ │ APP "Users" │ │
│ │ - Homepage │ │ - Articles │ │ - Login │ │
│ │ - Base Tpl │ │ - Comments │ │ - Register │ │
│ │ - Utils │ │ - Tags │ │ - Profile │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬────────┘ │
│ │ │ │ │
└────────┼──────────────────┼──────────────────┼────────────┘
│ │ │
▼ ▼ ▼
┌───────────────────────────────────────────────────┐
│ ORM (Couche d'abstraction) │
└───────────────────────────────────────────────────┘
▼ ▼ ▼
[Table: core_*] [Table: blog_*] [Table: users_*]
Chaque app gère ses propres tables en BDD, préfixées par le nom de l'app.
1. Création du Projet (La bonne méthode)
L'erreur classique est de créer des dossiers imbriqués inutiles.
# ❌ Mauvaise pratique (crée projet/projet/manage.py)
django-admin startproject mysite
# ✅ Bonne pratique (le point final est crucial)
mkdir ideolab_project
cd ideolab_project
django-admin startproject config .
Pourquoi appeler le dossier de config config au lieu du nom du projet ?
- C'est une convention moderne.
- Ça distingue clairement la configuration des applications métier.
- Ça simplifie les imports (ex:
from config import settings).
2. Création des Apps
python manage.py startapp core
python manage.py startapp accounts
python manage.py startapp dashboard
3. Le Registre des Apps (INSTALLED_APPS)
Si une app n'est pas ici, Django l'ignore (pas de migrations, pas de templates, pas de tests).
# config/settings.py
INSTALLED_APPS = [
# Apps natives Django (Batteries)
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Apps Tiers (Libs installées via pip)
'rest_framework',
'debug_toolbar',
# Vos Apps Locales (Ordre important pour l'override)
'core.apps.CoreConfig', # Recommandé: utiliser la config class
'accounts.apps.AccountsConfig',
'dashboard.apps.DashboardConfig',
]
Anatomie détaillée d'un projet Django 5.x
Chaque fichier a un rôle précis dans le cycle de vie.
📁 Niveau Racine (Root)
| Fichier | Fonction |
|---|---|
manage.py | Wrapper autour de django-admin. Utilise les settings du projet. Ne jamais modifier. |
db.sqlite3 | BDD fichier par défaut. À exclure du Git. |
requirements.txt | Liste des dépendances pip (pour le déploiement). |
.env | Variables d'environnement (Secret key, DB password). |
📁 Dossier de Config (config/)
| Fichier | Fonction |
|---|---|
settings.py | Le cerveau. Tout se décide ici (Middleware, Apps, DB). |
urls.py | La table des matières. Aiguille les requêtes vers les Apps. |
wsgi.py | (Web Server Gateway Interface). Point d'entrée pour Apache/Gunicorn (Synchrone). |
asgi.py | (Asynchronous SGI). Point d'entrée pour Daphne/Uvicorn (Websockets, Async). |
📁 Dossier App (ex: core/)
core/
├── migrations/ # Historique SQL (Versionning de la BDD)
│ └── 0001_initial.py
├── __init__.py # Fait du dossier un package Python
├── admin.py # Configuration de l'interface /admin/
├── apps.py # Métadonnées (Nom verbose, Signaux au démarrage)
├── models.py # ORM: Définition des tables et relations
├── tests.py # Tests unitaires (unittest/pytest)
├── views.py # Logique métier (Request -> Response)
├── urls.py # (Optionnel mais recommandé) Routes locales
├── forms.py # (À créer) Gestion des formulaires & validation
├── signals.py # (À créer) Hooks événementiels
└── management/ # (À créer) Commandes custom manage.py
└── commands/
Le Flux de contrôle (Dispatcher)
Comment une URL devient une page HTML ? Mécanisme de délégation.
1. URL Globale (config/urls.py)
On "monte" les apps sur des préfixes.
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
# Délégation: Tout ce qui commence par "blog/"
# est envoyé à l'app blog.
path('blog/', include('blog.urls')),
# Délégation: La racine est gérée par core
path('', include('core.urls')),
]
2. URL Locale (blog/urls.py)
On définit les routes finales et les variables.
from django.urls import path
from . import views
# app_name permet le reverse routing : {% url 'blog:detail' %}
app_name = 'blog'
urlpatterns = [
# Match: /blog/
path('', views.post_list, name='list'),
# Match: /blog/42/ (Path converters)
path('<int:id>/', views.post_detail, name='detail'),
# Match: /blog/slug-titre/
path('<slug:slug>/', views.post_by_slug, name='slug_detail'),
]
3. La Vue (Function Based View vs Class Based View)
Function Based View (FBV)
Simple, explicite, idéal pour débuter ou pour de la logique complexe spécifique.
from django.shortcuts import render, get_object_or_404
from .models import Post
def post_detail(request, id):
# Logique métier
post = get_object_or_404(Post, pk=id)
context = {'post': post}
return render(request, 'blog/detail.html', context)
Class Based View (CBV)
Puissant, concis, orienté objet. Idéal pour les CRUD standards.
from django.views.generic import DetailView
from .models import Post
class PostDetailView(DetailView):
model = Post
template_name = 'blog/detail.html'
context_object_name = 'post'
# Gère automatiquement le 404 et la requête
Préparer la production dès le jour 1
1. Séparer les Settings
Au lieu d'un seul settings.py, créez un package.
config/
└── settings/
├── __init__.py
├── base.py # Config commune (Apps, Auth...)
├── dev.py # Debug=True, Console Email
└── prod.py # Debug=False, S3, Sentry
Lancer avec : python manage.py runserver --settings=config.settings.dev
2. Le fichier .gitignore (Vital)
Ne committez jamais ces fichiers :
# Python
__pycache__/
*.py[cod]
venv/
env/
# Django
*.log
local_settings.py
db.sqlite3
media/
# Environment variables
.env
3. Organisation des Templates et Static
Bien que Django permette de mettre les templates dans chaque app, il est souvent plus propre d'avoir un dossier global à la racine pour surcharger facilement.
# settings.py
TEMPLATES = [{
'DIRS': [BASE_DIR / 'templates'], # Dossier global
'APP_DIRS': True, # Cherche aussi dans app/templates/
# ...
}]
# Structure
templates/
├── base.html # Squelette global
├── components/ # Navbar, Footer
├── blog/ # Surcharge app blog
└── accounts/ # Surcharge app accounts
Les Fondamentaux
Ces variables définissent l'identité et la sécurité du projet.
BASE_DIR
from pathlib import Path
# Pointe vers la racine du projet (là où est manage.py)
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY
# CRITIQUE : Utilisé pour signer les cookies, sessions, CSRF.
# Ne JAMAIS commiter la vraie clé.
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
DEBUG
Le paramètre le plus dangereux.
True(Dev) : Affiche des pages d'erreur détaillées (avec extraits de code, variables, SQL).False(Prod) : Affiche "Server Error (500)". Obligatoire en production.
ALLOWED_HOSTS
Liste blanche des domaines/IPs autorisés à servir le site (Protection Host Header Injection).
if DEBUG:
ALLOWED_HOSTS = [] # Autorise localhost, 127.0.0.1, [::1]
else:
ALLOWED_HOSTS = ['ideo-lab.com', 'www.ideo-lab.com', '192.168.1.50']
Gestion des Fichiers (Static & Media)
Comprendre la différence entre static (code) et media (uploads utilisateur) est vital.
Fichiers Statiques (CSS, JS, IMG du thème)
# URL publique (ex: /static/css/style.css)
STATIC_URL = 'static/'
# Où Django cherche les fichiers en DEV
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
# Où Django RASSEMBLE les fichiers en PROD
# (via la commande "collectstatic")
STATIC_ROOT = BASE_DIR / 'staticfiles'
Fichiers Media (Uploads Users)
# URL publique (ex: /media/avatars/user1.jpg)
MEDIA_URL = 'media/'
# Dossier physique de stockage
MEDIA_ROOT = BASE_DIR / 'media'
Note: En production, Nginx servira directement STATIC_ROOT et MEDIA_ROOT.
Templates
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'], # Pour utiliser un dossier global
'APP_DIRS': True, # Cherche aussi dans app/templates/
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request', # Requis par Allauth/Admin
'django.contrib.auth.context_processors.auth', # Ajoute "user" dans les tpl
'django.contrib.messages.context_processors.messages',
],
},
},
]
Base de Données (DATABASES)
Django supporte officiellement PostgreSQL, MariaDB, MySQL, Oracle et SQLite.
Option A: SQLite (Dev)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
Option B: PostgreSQL (Prod/Recommandé)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'ideo_db',
'USER': 'ideo_user',
'PASSWORD': os.environ.get('DB_PASS'),
'HOST': 'localhost',
'PORT': '5432',
}
}
Internationalisation (I18N)
# Code langue par défaut (français)
LANGUAGE_CODE = 'fr-fr'
# Fuseau horaire (Stockage UTC en base, affichage local)
TIME_ZONE = 'Europe/Paris'
# Activer la traduction
USE_I18N = True
# Activer le support des timezones (Recommandé: True)
USE_TZ = True
Si USE_TZ = True, Django stocke tout en UTC. La conversion vers 'Europe/Paris' se fait uniquement à l'affichage dans le template.
Professionnaliser la gestion des settings
Ne modifiez plus settings.py à la main pour passer de Dev à Prod. Utilisez ces outils.
1. python-dotenv (Le Standard Simple)
Charge les variables d'un fichier .env dans os.environ.
# Installation
pip install python-dotenv
# Dans manage.py et wsgi.py (au tout début)
from dotenv import load_dotenv
load_dotenv() # Cherche .env à la racine
# Dans settings.py
import os
SECRET_KEY = os.getenv('SECRET_KEY')
2. django-environ (Le "Douze Factor App")
Plus puissant : gère le typage (bool, int, list) et les schémas de BDD (DATABASE_URL).
# Installation
pip install django-environ
# Dans settings.py
import environ
env = environ.Env(
# Valeurs par défaut
DEBUG=(bool, False)
)
environ.Env.read_env(BASE_DIR / '.env')
DEBUG = env('DEBUG') # Convertit auto "True" en bool
# Parse l'URL postgres://user:pass@host:port/db
DATABASES = {'default': env.db()}
Exemple de fichier .env (à ne pas commiter !)
DEBUG=True
SECRET_KEY=votre-super-cle-secrete-inviolable
DATABASE_URL=postgres://ideo:password@localhost:5432/ideo_db
ALLOWED_HOSTS=127.0.0.1,localhost
Architecture "Split Settings"
Pour les gros projets, un seul fichier settings.py devient illisible. Voici comment le diviser.
my_project/
└── settings/
├── __init__.py # Vide (ou logique de sélection)
├── base.py # (Base) Apps, Auth, I18N, Templates
├── dev.py # (Dev) Debug=True, SQLite, Debug Toolbar, Console Email
├── prod.py # (Prod) Debug=False, PostgreSQL, S3, Sentry, Redis
└── test.py # (Test) Settings optimisés pour la rapidité des tests
Comment lier les fichiers ?
Dans dev.py et prod.py, on importe tout depuis base.
# settings/dev.py
from .base import *
DEBUG = True
ALLOWED_HOSTS = ['localhost']
# Outils de dev uniquement
INSTALLED_APPS += ['debug_toolbar']
MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']
Comment lancer Django ?
Il faut spécifier quel fichier de settings utiliser.
# En développement
python manage.py runserver --settings=my_project.settings.dev
# En production (Gunicorn/WSGI)
export DJANGO_SETTINGS_MODULE=my_project.settings.prod
gunicorn my_project.wsgi:application
django-split-settings permet de faire des inclusions encore plus propres (via glob patterns), mais l'héritage Python simple ci-dessus couvre 95% des besoins.Pourquoi découper en Apps ?
Dans un projet Django "Pro", on ne met pas tout dans une seule app. On sépare les responsabilités pour faciliter la maintenance, les tests et le travail en équipe.
Le "Standard IDEO-Lab" repose sur 4 piliers :
- Core : Ce qui est partagé (Templates de base, Modèles abstraits, Mixins).
- Accounts : Gestion des utilisateurs (Custom User, Auth, Profils).
- Dashboard : L'espace privé post-login (Stats, CRUD métier).
- Tools : Scripts, tâches de fond, analyseurs.
Diagramme de Dépendance
┌──────────────────┐
│ Projet (Root) │
└────────┬─────────┘
│
┌─────▼──────┐
│ APP "Core" │ (Base Templates, Utils)
└─────┬──────┘
│ ▲
┌─────────┤ │ (Hérite de Core)
│ ▼ │
┌─────▼────┐ ┌───┴────────┐
│ Accounts │ │ Dashboard │ ◀── (Dépend de Accounts)
└──────────┘ └────────────┘
▲ │
└─────────────┘
App "Core" : Les fondations invisibles
Cette app ne contient souvent pas d'URLs, mais fournit des outils pour les autres apps.
1. Modèles Abstraits (DRY)
Ne répétez jamais created_at et updated_at dans chaque modèle. Créez un modèle parent.
# core/models.py
from django.db import models
class TimeStampedModel(models.Model):
"""
Classe de base abstraite qui ajoute
automatiquement les champs de date.
"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=True)
class Meta:
abstract = True # Django ne créera pas de table pour ceci
2. Mixins de Vues
Ajoutez des fonctionnalités communes à vos Vues.
# core/mixins.py
from django.contrib.auth.mixins import UserPassesTestMixin
class SuperUserRequiredMixin(UserPassesTestMixin):
def test_func(self):
return self.request.user.is_superuser
3. Templates Globaux
Structure recommandée :
templates/
├── base.html # Squelette (Header/Footer)
├── partials/ # Fragments (Navbar, Sidebar)
│ ├── navbar.html
│ └── sidebar.html
└── core/ # Pages génériques
├── home.html
└── 404.html
App "Accounts" : Gestion de l'identité
C'est la première app à coder. Elle remplace le User par défaut de Django.
1. Custom User Model (OBLIGATOIRE)
Toujours faire ça avant la première migration !
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
# On ajoute nos champs spécifiques
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
bio = models.TextField(max_length=500, blank=True)
is_verified = models.BooleanField(default=False)
def __str__(self):
return self.username
N'oubliez pas AUTH_USER_MODEL = 'accounts.User' dans settings.py.
2. Vues d'Authentification
# accounts/urls.py
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
urlpatterns = [
path('login/', auth_views.LoginView.as_view(
template_name='accounts/login.html'
), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('register/', views.SignUpView.as_view(), name='register'),
]
App "Dashboard" : Le cœur de l'application
C'est ici que les utilisateurs connectés atterrissent. C'est le "Back-office client".
1. La Vue Principale (Protégée)
# dashboard/views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from core.models import Metric # Exemple
@login_required # Sécurité basique
def dashboard_home(request):
context = {
'user_count': 150,
'recent_activities': [],
'page_title': 'Tableau de bord'
}
return render(request, 'dashboard/home.html', context)
2. Structure des Templates
Le dashboard a souvent un layout différent du site public (Sidebar vs Topbar).
templates/dashboard/
├── base_dashboard.html # Étend base.html mais ajoute la Sidebar
├── home.html # Contenu spécifique
└── profile/
└── edit.html
3. Exemple de template
{% extends "dashboard/base_dashboard.html" %}
{% block content %}
Bienvenue
{% endblock %}
App "Tools" : L'atelier technique
Une app pour ranger vos scripts, commandes de gestion et utilitaires système.
1. Commandes de Gestion (Management Commands)
Permet de lancer des tâches via python manage.py ...
# tools/management/commands/cleanup_logs.py
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = 'Nettoie les vieux logs'
def handle(self, *args, **kwargs):
# Logique de nettoyage
self.stdout.write(self.style.SUCCESS('Logs nettoyés !'))
Usage : python manage.py cleanup_logs
2. Template Tags personnalisés
Ajoutez des fonctions utilisables dans les templates HTML.
# tools/templatetags/ideo_extras.py
from django import template
import datetime
register = template.Library()
@register.filter(name='days_until')
def days_until(date):
delta = date - datetime.date.today()
return delta.days
Usage HTML : {{ project.deadline|days_until }} jours restants
Comment Django traite une URL ?
Quand une requête arrive (ex: /blog/2024/django-tuto/), Django suit ce processus :
- Il charge le module défini dans
ROOT_URLCONF(généralementconfig/urls.py). - Il parcourt la liste
urlpatterns**dans l'ordre**, du haut vers le bas. - Il s'arrête à la **première correspondance**.
- Si ça matche un
include(), il coupe la partie matchée et envoie le reste à l'URLconf de l'app.
Diagramme de Délégation
URL Demandée: /blog/articles/42/
[ROOT_URLCONF] (config/urls.py)
│
├── path('admin/', ...) ❌ (Pas de match)
│
├── path('blog/', include('blog.urls')) ✅ MATCH !
│ │
│ └─ Django coupe "/blog/" et envoie "articles/42/"
│ à blog.urls
│
▼
[APP_URLCONF] (blog/urls.py)
│
├── path('', ...) ❌ (Attendait vide, reçu "articles/42/")
│
├── path('articles//', views.detail) ✅ MATCH !
│
└─ Exécute: views.detail(request, pk=42)
Capturer des arguments typés
Fini les Regex illisibles. Utilisez les "Path Converters" pour capturer et typer les variables d'URL.
| Converter | Type Python | Exemple URL | Match |
|---|---|---|---|
<int:id> | int | /user/42/ | ✅ |
<str:name> | str | /user/john/ | ✅ (Sauf le séparateur /) |
<slug:slug> | str | /post/mon-super-titre/ | ✅ (Lettres, chiffres, tirets) |
<uuid:uid> | UUID | /detail/a0eebc99-9c0b.../ | ✅ (Format UUID strict) |
<path:file> | str | /files/dossier/img.jpg | ✅ (Inclut le séparateur /) |
Exemple Standard
# urls.py
path('user/<str:username>/', views.profile),
path('archive/<int:year>/<int:month>/', views.archive),
# views.py
def profile(request, username): # username est un str
...
def archive(request, year, month): # year est un int
...
Regex Complexes (re_path)
Pour des besoins très spécifiques (ex: ID hexadécimal de longueur fixe).
from django.urls import re_path
# Match IDs de 4 caractères alphanumériques
re_path(r'^sku/(?P<sku>[0-9a-f]{4})/$', views.sku_detail),
Le problème des conflits
Si l'app "Blog" a une vue nommée detail et l'app "Shop" a aussi une vue nommée detail, comment faire un lien vers le bon détail ?
{% url 'detail' id=1 %} est ambigu !
La Solution : app_name
Définissez app_name dans le urls.py de chaque application.
# blog/urls.py
app_name = 'blog' # <--- ICI
urlpatterns = [
path('<int:pk>/', views.detail, name='detail'),
]
# shop/urls.py
app_name = 'shop' # <--- ICI
urlpatterns = [
path('<int:pk>/', views.detail, name='detail'),
]
Utilisation
On utilise la syntaxe namespace:name.
- Vers le blog :
blog:detail - Vers le shop :
shop:detail
Lire l'article Acheter
Le principe DRY (Don't Repeat Yourself)
Ne jamais écrire d'URLs en dur (ex: /blog/12/) dans votre code. Si l'URL change demain, vous devrez modifier 50 fichiers.
Laissez Django reconstruire l'URL à partir du nom de la vue et des paramètres.
Dans les Templates ({% url ... %})
Accueil Voir {% url 'shop:cart' as cart_url %} {% if cart_url %} Panier {% endif %}
Dans le Code Python (reverse)
from django.urls import reverse
from django.shortcuts import redirect
def my_view(request):
# 1. Générer l'URL en string
url = reverse('blog:detail', kwargs={'pk': 42})
# Résultat : "/blog/articles/42/"
# 2. Rediriger directement
return redirect('blog:list')
# 3. Dans les tests
response = self.client.get(reverse('home'))
get_absolute_url() dans vos Modèles. Cela permet d'appeler dans les templates sans se soucier de l'URLconf.Organisation recommandée
templates/
base.html
core/
home.html
about.html
accounts/
login.html
profile.html
static/
css/app.css
js/app.js
img/logo.png
On centralise les templates dans templates/ pour tout le projet.
Template de base
{%!DOCTYPE html%}
{% block title %}IDEO‑Lab{% endblock %}
{% include "partials/navbar.html" %}
{% block content %}{% endblock %}
Template d'une page
{% extends "base.html" %}
{% block title %}Accueil{% endblock %}
{% block content %}
Bienvenue
Votre premier projet Django est fonctionnel.
{% endblock %}Fichiers statiques
En production : python manage.py collectstatic pour copier tous les statiques dans STATIC_ROOT.
Créer un modèle
# core/models.py
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.titleMigrations
python manage.py makemigrations
python manage.py migrateUtiliser l'ORM
from core.models import Article
# créer
Article.objects.create(title="Hello", slug="hello", content="Bienvenue")
# récupérer
art = Article.objects.get(slug="hello")
# filtrer
arts = Article.objects.filter(title__icontains="Django").order_by("-created")
Dans une view
# core/views.py
from django.shortcuts import render
from .models import Article
def blog(request):
articles = Article.objects.all()
return render(request, "core/blog.html", {"articles": articles})Par défaut : SQLite
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}Très bien pour débuter ou pour petites apps internes.
PostgreSQL
pip install psycopg2-binary
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "ideolab_db",
"USER": "ideolab_user",
"PASSWORD": "motdepasse",
"HOST": "127.0.0.1",
"PORT": "5432",
}
}MariaDB / MySQL
pip install mysqlclient # ou django-mysql
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "ideolab_db",
"USER": "ideolab",
"PASSWORD": "xxx",
"HOST": "localhost",
"PORT": "3306",
"OPTIONS": {
"init_command": "SET sql_mode='STRICT_TRANS_TABLES'",
}
}
}Tests de connexion
python manage.py migrate
python manage.py dbshell # selon driverActiver un modèle
# core/admin.py
from django.contrib import admin
from .models import Article
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ("title", "slug", "created", "updated")
search_fields = ("title", "content")
prepopulated_fields = {"slug": ("title",)}
Créer un superuser :
python manage.py createsuperuser
Accès à /admin
L'admin est déjà branchée dans urls.py. On peut la styliser ou utiliser django-jazzmin pour un admin moderne.
pip install django-jazzmin
# puis ajouter "jazzmin" dans INSTALLED_APPS avant django.contrib.adminL'admin est très utile pour prototyper des modèles.
Form simple
# core/forms.py
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
Dans la view
# core/views.py
from .forms import ContactForm
def contact(request):
form = ContactForm(request.POST or None)
if request.method == "POST" and form.is_valid():
# traiter ...
pass
return render(request, "core/contact.html", {"form": form})ModelForm
# core/forms.py
from django.forms import ModelForm
from .models import Article
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ["title", "slug", "content"]
Permet de générer un formulaire directement lié à un modèle.
Login / Logout
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, redirect
def login_view(request):
if request.method == "POST":
u = request.POST.get("username")
p = request.POST.get("password")
user = authenticate(request, username=u, password=p)
if user is not None:
login(request, user)
return redirect("home")
return render(request, "accounts/login.html")
def logout_view(request):
logout(request)
return redirect("login")Protéger une vue
from django.contrib.auth.decorators import login_required
@login_required
def dashboard(request):
...Permissions
from django.contrib.auth.decorators import permission_required
@permission_required("core.change_article")
def edit_article(request, pk):
...Installation DRF
pip install djangorestframework
# settings.py
INSTALLED_APPS += ["rest_framework"]Serializer
# core/api/serializers.py
from rest_framework import serializers
from core.models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = "__all__"
ViewSet
# core/api/views.py
from rest_framework import viewsets
from .serializers import ArticleSerializer
from core.models import Article
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
Routing
# core/api/urls.py
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet
router = DefaultRouter()
router.register("articles", ArticleViewSet)
urlpatterns = router.urlsCommande
python manage.py runserver
# ou spécifier le port
python manage.py runserver 0.0.0.0:8000Utiliser ce serveur uniquement pour le développement.
Recharger automatiquement
Django recharge automatiquement quand un fichier Python change. Pour le frontend pur, utiliser aussi un watcher (npm/vite) si besoin.
Gunicorn – rôle
Gunicorn (Green Unicorn) est le serveur WSGI qui exécute Django. Nginx envoie les requêtes vers lui, il renvoie la réponse HTML/JSON.
Structure typique
/opt/ideo-lab/
├── venv/
├── ideolab_site/
├── staticfiles/
└── media/Lancer en manuel (débug)
cd /opt/ideo-lab
source venv/bin/activate
gunicorn ideolab_site.wsgi:application --bind 0.0.0.0:8001 --workers 3 --log-level infoToujours tester d'abord en manuel. Si ça plante ici, ce n'est pas la peine de brancher Nginx.
Service systemd (classique)
# /etc/systemd/system/gunicorn_ideo.service
[Unit]
Description=Gunicorn daemon for IDEO‑Lab
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/ideo-lab
EnvironmentFile=/opt/ideo-lab/.env
ExecStart=/opt/ideo-lab/venv/bin/gunicorn --workers 4 --threads 2 --timeout 120 --bind unix:/run/gunicorn_ideo.sock ideolab_site.wsgi:application
Restart=always
[Install]
WantedBy=multi-user.targetService socket‑based
# /etc/systemd/system/gunicorn_ideo.socket
[Unit]
Description=Gunicorn IDEO‑Lab socket
[Socket]
ListenStream=/run/gunicorn_ideo.sock
[Install]
WantedBy=sockets.targetCommandes
sudo systemctl daemon-reload
sudo systemctl enable gunicorn_ideo
sudo systemctl start gunicorn_ideo
sudo systemctl status gunicorn_ideoSi le socket n'existe pas → vérifier les permissions /run, l'utilisateur systemd et le chemin du venv.
Optimisation workers
# règle générale
workers = (2 * nombre_coeurs) + 1
threads = 2
timeout = 120Exemple : serveur 4 vCPU → (2×4)+1 = 9 workers. Vérifier la RAM (100‑120 Mo / worker).
Tuning chemin Python
ExecStart=/opt/ideo-lab/venv/bin/gunicorn ...
Toujours pointer vers le binaire gunicorn du venv du projet.
Logging avancé
ExecStart=/opt/ideo-lab/venv/bin/gunicorn --access-logfile /var/log/gunicorn/access.log --error-logfile /var/log/gunicorn/error.log ideolab_site.wsgi:application
Les logs Gunicorn peuvent être agrégés dans journald ou envoyés vers Loki/Promtail.
Monitoring
- Module
gunicorn --statsd-host=localhost:8125pour exporter les métriques - Supervision via systemd-analyze ou htop
- Surveillance du socket avec ss -lpt | grep gunicorn
Dépannage rapide
journalctl -u gunicorn_ideo -f
sudo systemctl restart gunicorn_ideo
sudo ss -lpt | grep gunicornSchéma
Nginx → (unix socket) → Gunicorn → Django → DB
Exemple simple (HTTP, socket local)
server {
listen 80;
server_name ideo-lab.com www.ideo-lab.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /opt/ideo-lab;
}
location /media/ {
root /opt/ideo-lab;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn_ideo.sock;
}
}Activation
sudo ln -s /etc/nginx/sites-available/ideo-lab /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxHTTPS avec Certbot (Let's Encrypt)
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d ideo-lab.com -d www.ideo-lab.comCertbot crée automatiquement la configuration SSL et renouvelle les certificats.
Version complète HTTPS
server {
listen 443 ssl http2;
server_name ideo-lab.com www.ideo-lab.com;
ssl_certificate /etc/letsencrypt/live/ideo-lab.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ideo-lab.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
add_header Strict-Transport-Security "max-age=31536000" always;
client_max_body_size 50M;
keepalive_timeout 65;
access_log /var/log/nginx/ideo_access.log;
error_log /var/log/nginx/ideo_error.log;
location /static/ {
alias /opt/ideo-lab/staticfiles/;
expires 1y;
add_header Cache-Control "public, immutable";
}
location /media/ {
alias /opt/ideo-lab/media/;
}
location / {
proxy_pass http://unix:/run/gunicorn_ideo.sock;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 600s;
proxy_connect_timeout 600s;
}
}Redirection HTTP → HTTPS
server {
listen 80;
server_name ideo-lab.com www.ideo-lab.com;
return 301 https://$host$request_uri;
}Multi‑site (plusieurs projets Django)
# /etc/nginx/sites-available/
├── ideo-lab.conf
├── blog.conf
└── api.confChaque site utilise son propre socket Gunicorn : /run/gunicorn_blog.sock, /run/gunicorn_api.sock. On peut mutualiser les logs et les certificats via certbot --expand.
Reverse proxy + cache (avancé)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=24h;
server {
location /static/ {
proxy_cache STATIC;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_pass http://unix:/run/gunicorn_ideo.sock;
}
}Diagnostic & maintenance
sudo nginx -tpour valider la configsudo systemctl reload nginxpour recharger sans coupuresudo tail -f /var/log/nginx/access.logpour suivre les requêtessudo journalctl -u nginxpour logs système
Schéma du flux
Client HTTPS
│
▼
Nginx (TLS, static, cache)
│
▼
Gunicorn (WSGI)
│
▼
Django ORM → PostgreSQL/MariaDBContexte
Sous Windows, on peut garder le serveur de dev, mais pour une intégration avec Apache on utilisera mod_wsgi.
Installer mod_wsgi pour Windows
Télécharger la version compatible avec Apache + Python, puis ajouter dans httpd.conf.
LoadModule wsgi_module modules/mod_wsgi.so
WSGIPythonHome "C:/path/to/venv/"VirtualHost
ServerName django.local WSGIScriptAlias / C:/projects/ideo-lab/ideolab_site/wsgi.py Alias /static/ C:/projects/ideo-lab/static/ Require all granted Require all granted
Moins courant qu'un déploiement Linux, mais utile en environnement 100% Windows.
Variables d'environnement
Ne jamais mettre les mots de passe dans settings.py. Utiliser python-dotenv ou l'environnement systemd.
pip install python-dotenv
from dotenv import load_dotenv
import os
load_dotenv()
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY")
DEBUG = os.getenv("DJANGO_DEBUG", "False") == "True"Exemple de .env
DJANGO_SECRET_KEY=...long...
DJANGO_DEBUG=False
ALLOWED_HOSTS=ideo-lab.com,www.ideo-lab.com
DATABASE_URL=postgres://user:pass@localhost:5432/ideo_lab
SENTRY_DSN=https://xxxxx.ingest.sentry.io/xxxxGestion des secrets sur le serveur
sudo mkdir /opt/ideo-lab/secrets
sudo chown www-data:www-data /opt/ideo-lab/secrets
sudo chmod 600 /opt/ideo-lab/secretsParamètres de sécurité Django
DEBUG = False
ALLOWED_HOSTS = ["ideo-lab.com", "www.ideo-lab.com"]
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = "DENY"CSP (Content Security Policy)
class CSPMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
resp = self.get_response(request)
resp["Content-Security-Policy"] = "default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'"
return respLogging multi‑niveau
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "[{asctime}] {levelname} {name}: {message}",
"style": "{",
},
},
"handlers": {
"file": {
"class": "logging.FileHandler",
"filename": BASE_DIR / "logs/django_info.log",
"formatter": "verbose",
},
"error_file": {
"class": "logging.FileHandler",
"filename": BASE_DIR / "logs/django_errors.log",
"formatter": "verbose",
},
},
"loggers": {
"django": {
"handlers": ["file"],
"level": "INFO",
"propagate": True,
},
"django.request": {
"handlers": ["error_file"],
"level": "ERROR",
"propagate": False,
},
},
}Audit cron
#!/bin/bash
CONF="/opt/ideo-lab/ideolab_site/settings.py"
LOG="/opt/ideo-lab/logs/security_audit.log"
grep -q "DEBUG = True" $CONF && echo "$(date) - ALERT: DEBUG activé" >> $LOG
find /opt/ideo-lab/logs/ -size +50M -type f -exec echo "$(date) - Log volumineux: {}" \; >> $LOGIntégration Sentry
pip install sentry-sdk
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn=os.getenv("SENTRY_DSN"),
integrations=[DjangoIntegration()],
traces_sample_rate=0.3,
send_default_pii=True,
)Toutes les erreurs de production sont alors visibles dans le dashboard Sentry (avec user, URL, stacktrace).
Collecte des statiques
python manage.py collectstatic
À lancer après chaque déploiement si tu as modifié les fichiers statiques.
Maintenance DB
python manage.py showmigrations
python manage.py migrate
python manage.py clearsessionsSupervision
Sur Linux : superviser gunicorn et nginx (systemd). Ajouter un healthcheck.
Cache
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "unique-snowflake",
}
}Arbo serveur (Ubuntu)
/opt/ideo-lab/
venv/
ideolab_site/
staticfiles/
media/
/etc/systemd/system/gunicorn_ideo.service
/etc/nginx/sites-available/ideo-lab
Pipeline
git pull origin main
source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py collectstatic --noinput
sudo systemctl restart gunicorn_ideo
sudo systemctl restart nginxSchéma de flux
Client (HTTPS)
│
▼
Nginx (SSL, static, media)
│ proxy_pass
▼
Gunicorn (WSGI, Django)
│
▼
Django → ORM → DB (PostgreSQL/MariaDB)
On peut ensuite automatiser avec GitHub Actions ou un cron qui fait le pull + reload.
Commandes les plus utiles
django-admin startproject proj .
python manage.py startapp app
python manage.py runserver
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py shell
python manage.py collectstatic
Outils debug
pip install django-debug-toolbar
# settings.py
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")Très utile en dev pour voir les requêtes SQL.
