Objectif & contexte
Sur les gros projets Django (multi-apps), les migrations peuvent devenir un point de friction : migrations “fantômes”, branches Git divergentes, fichiers en double, dépendances en chaîne, ou encore un historique devenu inutile en DEV.
- Automatiser un nettoyage “propre” (DEV/TEST) en une commande utilisable en cron
- Éviter les manips manuelles longues et risquées dans
migrations/ - Repartir d’un état cohérent (scripts + table
django_migrations) - Optionnel : sécuriser l’opération par backup, git commit et dump DB
Pipeline (ordre des étapes)
0) Precheck (sécurité) :
- refuse si migrations non appliquées
- refuse si des changements de models nécessitent de nouvelles migrations
(Optionnel) Git commit (snapshot code)
(Optionnel) Dump DB (snapshot base)
1) Supprimer migrations/*.py (sauf __init__.py)
2) Supprimer __pycache__ sous migrations/
3) Vider django_migrations (tout ou apps ciblées)
4) python manage.py makemigrations (tout ou apps ciblées)
5) python manage.py migrate --fake (tout ou apps ciblées)Quand l’utiliser ?
- DEV : tu veux repartir à zéro “propre”
- Tu as des migrations fantômes / répétées
- Tu veux synchroniser un environnement après gros refactor
- Tu veux automatiser un reset périodique (environnement de test)
Quand NE PAS l’utiliser
- Production (sauf cas ultra contrôlé, exceptionnel)
- Quand tu as des migrations manuelles critiques (RunSQL/RunPython)
- Quand plusieurs équipes partagent le même historique de migrations
- Quand tu veux conserver une traçabilité stricte des évolutions
Fichiers livrés (Toolbox)
/static/toolbox/migration_cleanup.py
Télécharger wrapper cron (optionnel)/static/toolbox/run_migration_cleanup.sh
Installation (Django)
1) Emplacement du command
Place le fichier dans une app “toolbox” (ou “core”) :
toolbox/
management/
__init__.py
commands/
__init__.py
migration_cleanup.py2) Vérifier que Django le détecte
python manage.py help | grep migration_cleanup
python manage.py migration_cleanup --help3) Préparer les dossiers
Si tu utilises les backups / dumps :
sudo mkdir -p /var/backups/ideo-lab
sudo chown -R ubuntu:ubuntu /var/backups/ideo-lab
sudo mkdir -p /var/log/ideo-lab~/.my.cnf (évite les prompts de mot de passe).# ~/.my.cnf (permissions 600)
[client]
user=ideolab
password=********
host=127.0.0.1
port=3306Mode d’emploi exact
--force. C’est volontaire pour éviter les accidents.Cas standard (recommandé)
Reset global + backup des migrations supprimées + progress “cron friendly”.
python manage.py migration_cleanup \
--database=default \
--force --noinput \
--backup-dir=/var/backups/ideo-lab \
--progress=dotsCibler une app
python manage.py migration_cleanup \
--database=default \
--application=translation \
--force --noinput \
--backup-dir=/var/backups/ideo-lab \
--progress=dots --show-tablesCibler un datamodel (déduit l’app)
Le “datamodel” sert surtout à sélectionner l’app. Django migre par app.
python manage.py migration_cleanup \
--database=default \
--datamodel=translation.SearchTemplate \
--force --noinput \
--progress=barCheck après exécution (indispensable)
# 1) Aucun changement model “en attente”
python manage.py makemigrations --check --dry-run
# 2) Plan des migrations : tout doit être “appliqué”
python manage.py showmigrations --planOptions & Flags (description complète)
| Option | Rôle | Conseils / remarques |
|---|---|---|
--force | Obligatoire : autorise l’opération destructrice | Sans ça : refus systématique (anti-accident). |
--noinput | Mode cron : pas d’interaction | À activer en cron. En manuel, tu peux le retirer. |
--database=ALIAS | Choisit l’alias DB Django | Ex: default, replica, etc. |
--application=app | Limite aux apps ciblées (répétable) | Recommandé pour un reset “local” (moins risqué). |
--datamodel=app.Model | Sélection d’app via modèle (répétable) | Pratique quand tu penses en “model”, pas en “app”. |
--backup-dir=/path | Copie les migrations supprimées (timestamp) | Très conseillé : récupère RunSQL/RunPython si oubli. |
--skip-precheck | Ignore la sécurité (dangereux) | À éviter. Utiliser seulement si tu sais EXACTEMENT pourquoi. |
--progress=bar|dots|none | Affichage progression | dots = logs cron propres. none = silence. |
--show-tables | Affiche les tables impactées (par app) | Best-effort via model._meta.db_table. |
- Si tu as des migrations non appliquées : il refuse (sinon tu masques un état incohérent)
- Si tes models ont changé sans migration : il refuse (sinon tu crées un reset “bancal”)
Automatiser Git commit & Dump DB (options)
- Snapshot du code : commit git automatique (optionnel)
- Snapshot de la DB : dump SQL (optionnel)
1) Git commit automatique
Option --git-commit : fait git add -A puis git commit avec un message. Par défaut, l’outil peut refuser si le repo est “dirty” (option de sécurité).
python manage.py migration_cleanup \
--database=default \
--force --noinput \
--git-commit --git-message="chore: reset migrations (auto)" \
--backup-dir=/var/backups/ideo-lab \
--progress=dots- Idéalement : utilise une branche “dev/reset-migrations” dédiée
- Le commit auto doit rester un “snapshot” (pas une release)
- Si tu as des modifications non liées : fais un stash/commit manuel avant
2) Dump MySQL / MariaDB automatique
Option --db-dump : lance un mysqldump / mariadb-dump avant purge. En cron, privilégie ~/.my.cnf.
python manage.py migration_cleanup \
--database=default \
--force --noinput \
--db-dump --dump-dir=/var/backups/ideo-lab \
--backup-dir=/var/backups/ideo-lab \
--progress=dots| Option dump | Description | Exemple |
|---|---|---|
--dump-dir | Répertoire des dumps (fichiers timestamp) | /var/backups/ideo-lab |
--dump-tool | Outil à utiliser (auto, mysqldump, mariadb-dump) | --dump-tool=mariadb-dump |
--dump-extra-args | Args additionnels (transaction, routines, triggers…) | --single-transaction --routines --triggers --events |
- Le répertoire est protégé (permissions)
- Tu as une politique de rotation (purge de vieux dumps)
- Tu n’exposes jamais ce dossier via le web
Mode “ultra safe” (commit + dump + backup)
python manage.py migration_cleanup \
--database=default \
--force --noinput \
--git-commit --git-message="chore: migration cleanup snapshot" \
--db-dump --dump-dir=/var/backups/ideo-lab \
--backup-dir=/var/backups/ideo-lab \
--progress=dotsCron : recommandations et exemples
- se place dans le bon dossier projet
- active le venv
- exporte les variables env (settings)
- écrit un log propre
Exemple (crontab) — reset DEV chaque dimanche
# Tous les dimanches à 03:10
10 3 * * 0 /static/toolbox/run_migration_cleanup.sh >> /var/log/ideo-lab/migration_cleanup.log 2>&1Wrapper bash (exemple)
À livrer dans /static/toolbox/run_migration_cleanup.sh (ou sur le serveur). Adapte les chemins : projet, venv, settings.
#!/usr/bin/env bash
set -euo pipefail
PROJECT_DIR="/srv/ideo-lab"
VENV_PY="/srv/ideo-lab/venv/bin/python"
export DJANGO_SETTINGS_MODULE="config.settings.dev" # adapte
export PYTHONUNBUFFERED=1
cd "$PROJECT_DIR"
# Optionnel : dump + git + backup
"$VENV_PY" manage.py migration_cleanup \
--database=default \
--force --noinput \
--git-commit --git-message="chore: migration cleanup snapshot" \
--db-dump --dump-dir=/var/backups/ideo-lab \
--backup-dir=/var/backups/ideo-lab \
--progress=dotsVariante : seulement une app (moins risqué)
"$VENV_PY" manage.py migration_cleanup \
--database=default \
--application=translation \
--force --noinput \
--backup-dir=/var/backups/ideo-lab \
--progress=dots --show-tablesAvantages, limites, risques
Avantages
- Gain de temps : fini le nettoyage manuel de dizaines de fichiers
- Réduction d’erreur : workflow standardisé, reproductible
- Cron-friendly : logs + noinput + progress
- Snapshot : backup migrations + dump DB + git commit (optionnel)
Limites
- Django migre “par app” : pas de reset “au modèle” uniquement
- Le
migrate --fakemarque l’état sans exécuter SQL - Si la DB est divergente “à la main”, tu peux masquer un problème
Risques
- Perte de migrations “custom” (RunSQL/RunPython) si pas de backup
- Traçabilité cassée (l’historique est réécrit)
- Danger prod : upgrades, audits, rollbacks deviennent délicats
--backup-dirsystématique--db-dumpsystématique (au moins sur environnements importants)--applicationplutôt que “ALL” quand tu peux- Après reset :
makemigrations --check --dry-run+showmigrations --plan
Dépannage (les cas fréquents)
1) “Refusing to run without --force”
python manage.py migration_cleanup --force --noinput2) Precheck failed: unapplied migrations exist
Tu as des migrations non appliquées. Applique-les d’abord (ou comprends pourquoi elles existent).
python manage.py showmigrations --plan
python manage.py migrate3) Precheck failed: model changes detected
Tes models ont changé mais les migrations ne sont pas générées.
python manage.py makemigrations
python manage.py makemigrations --check --dry-run4) Dump DB échoue en cron (auth)
~/.my.cnf (permissions 600).5) Git commit échoue (repo dirty)
Comportement volontaire pour éviter de committer du “bruit”.
# Option 1 : commit/stash manuel avant
git status
git add -A && git commit -m "wip"
# Option 2 : autoriser (si tu as prévu un mode allow-dirty)
python manage.py migration_cleanup --git-commit --git-allow-dirty ...6) Après reset : comportements bizarres
- Vérifie que tu es dans le bon settings / env
- Vérifie l’alias
--database - Vérifie :
python manage.py makemigrations --check --dry-run - Vérifie :
python manage.py showmigrations --plan
Cheat sheet
| Action | Commande |
|---|---|
| Voir plan migrations | python manage.py showmigrations --plan |
| Check changements models | python manage.py makemigrations --check --dry-run |
| Reset global (safe) | python manage.py migration_cleanup --force --noinput --backup-dir=/var/backups/ideo-lab |
| Reset app | python manage.py migration_cleanup --application=translation --force --noinput |
