Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

Toolbox Dev — Ideo-Lab

Django Migration Cleanup — Cron / Management Command

“Reset propre” des migrations (DEV/TEST) : suppression scripts → purge django_migrations → rebuild → migrate --fake, avec options de backup, git commit, dump DB et progress.

Ops / Django  •  MySQL/MariaDB  •  Cron-friendly

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.

But de cet outil :
  • 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
Attention : cette commande est destructrice. Elle est conçue pour DEV/TEST (ou environnements “jetables”). Sur prod, elle peut casser la traçabilité, les migrations custom (RunSQL/RunPython), et compliquer les upgrades.

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)
Reset migrations Cron Backup Fake migrate

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)

Télécharger migration_cleanup.py

/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.py

2) Vérifier que Django le détecte

python manage.py help | grep migration_cleanup
python manage.py migration_cleanup --help
Attendu →la commande apparaît avec ses options

3) 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
Conseil : sur MySQL/MariaDB, configure les credentials dump pour cron via ~/.my.cnf (évite les prompts de mot de passe).
# ~/.my.cnf (permissions 600)
[client]
user=ideolab
password=********
host=127.0.0.1
port=3306

Mode d’emploi exact

Règle n°1 : l’outil refuse d’agir sans --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=dots

Cibler une app

python manage.py migration_cleanup \
  --database=default \
  --application=translation \
  --force --noinput \
  --backup-dir=/var/backups/ideo-lab \
  --progress=dots --show-tables

Cibler 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=bar

Check 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 --plan
Objectif →No changes detected + showmigrations OK

Options & Flags (description complète)

OptionRôleConseils / remarques
--forceObligatoire : autorise l’opération destructriceSans ça : refus systématique (anti-accident).
--noinputMode cron : pas d’interactionÀ activer en cron. En manuel, tu peux le retirer.
--database=ALIASChoisit l’alias DB DjangoEx: default, replica, etc.
--application=appLimite aux apps ciblées (répétable)Recommandé pour un reset “local” (moins risqué).
--datamodel=app.ModelSélection d’app via modèle (répétable)Pratique quand tu penses en “model”, pas en “app”.
--backup-dir=/pathCopie les migrations supprimées (timestamp)Très conseillé : récupère RunSQL/RunPython si oubli.
--skip-precheckIgnore la sécurité (dangereux)À éviter. Utiliser seulement si tu sais EXACTEMENT pourquoi.
--progress=bar|dots|noneAffichage progressiondots = logs cron propres. none = silence.
--show-tablesAffiche les tables impactées (par app)Best-effort via model._meta.db_table.
Précheck (par défaut) — ce qu’il protège :
  • 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)

Philosophie : avant de faire une opération destructive, on crée un “snapshot” :
  • 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
Conseils Git :
  • 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 dumpDescriptionExemple
--dump-dirRépertoire des dumps (fichiers timestamp)/var/backups/ideo-lab
--dump-toolOutil à utiliser (auto, mysqldump, mariadb-dump)--dump-tool=mariadb-dump
--dump-extra-argsArgs additionnels (transaction, routines, triggers…)--single-transaction --routines --triggers --events
Important : un dump SQL peut contenir des données sensibles. Assure-toi que :
  • 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=dots

Cron : recommandations et exemples

Conseil : en cron, utilise un wrapper bash qui :
  • 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>&1

Wrapper 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=dots
Astuce →Préférer --progress=dots pour logs cron

Variante : 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-tables

Avantages, 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 --fake marque 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
Risque n°1 : tu as une migration qui faisait plus que “créer une table” (data migration, normalisation, backfill, indexes, contraintes…). Si tu l’effaces, tu perds la recette et tu peux créer une divergence silencieuse.
Mitigation recommandée :
  • --backup-dir systématique
  • --db-dump systématique (au moins sur environnements importants)
  • --application plutô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 --noinput

2) 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 migrate

3) 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-run

4) Dump DB échoue en cron (auth)

Cause n°1 : mysqldump demande un mot de passe mais cron ne peut pas répondre. Solution : ~/.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

Checklist :
  • 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

ActionCommande
Voir plan migrationspython manage.py showmigrations --plan
Check changements modelspython manage.py makemigrations --check --dry-run
Reset global (safe)python manage.py migration_cleanup --force --noinput --backup-dir=/var/backups/ideo-lab
Reset apppython manage.py migration_cleanup --application=translation --force --noinput