= South : Migration de schémas et de données pour Django = <> == INTRODUCTION == * Objectifs : * connaître les commandes de base pour migrer ses données * Pourquoi South? * schémas et données * migrations * changement sur la DB de version en version * Environnement technique * Installer South 0.7.3 à partir du tar.gz * [[http://www.aeracode.org/releases/south/south-0.7.3.tar.gz|south-0.7.3.tar.gz]] * extraire l'archive {{{ cd south python setup.py install }}} * vérification de l'installation {{{ ipython import south south.__version__ }}} * Informations officielles * [[http://south.aeracode.org/|Site officiel]] * [[http://south.aeracode.org/docs/|Documentation officielle]] * [[http://south.aeracode.org/docs/tutorial/|Tutoriel officiel]] * Ce tutoriel structure cet atelier ---- == Déployer South dans notre projet Django == {{{ cd espace-formation/bmo_repertoire }}} * settings.py {{{ INSTALLED_APPS = ( ... 'south', ) }}} * créer les tables de South {{{ python manage.py syncdb }}} ---- == Généralités == * South stocke migrations dans l'app * Si pas de migrations dans l'app : South == syncdb * South détecte changements aux modèles par rapport à la dernière migration * South suit la classe, les champs et les options des champs * Possible de revenir en arrière dans l'historique des migrations (et retourner en avant) * CRUD classe = CRUD table * CRUD champ = CRUD colonne (column) * CRUD donnée = CRUD rangée (row) ---- == TUTORIEL Partie 1 : les bases == === Première migration === * créer une nouvelle application "tutoriel" {{{ python manage.py startapp tutoriel }}} * settings.py {{{ INSTALLED_APPS = ( ... 'tutoriel', ) }}} * tutoriel/models.py {{{ class Etudiant(models.Model): nom = models.CharField(max_length=100) temps_complet = models.BooleanField() }}} * ne pas utiliser {{{ syncdb }}} * créer une '''première''' migration {{{ python manage.py schemamigration tutoriel --initial }}} * répertoire migrations/ créé avec migration dedans * appliquer la migration {{{ python manage.py migrate tutoriel }}} * vérification dans shell : {{{ python manage.py shell from tutoriel.models import Etudiant e = Etudiant() e. [+tab] # ipython }}} === Modifier le modèle === * ajouter un champ {{{ class Etudiant(models.Model): nom = models.CharField(max_length=100) temps_complet = models.BooleanField() dort_toujours = models.BooleanField() }}} * créer une migration qui consigne le changement {{{ python manage.py schemamigration tutoriel --auto }}} * --auto, '''pas''' --initial * appliquer la migration {{{ python manage.py migrate tutoriel }}} * vérification dans shell : {{{ python manage.py shell from tutoriel.models import Etudiant e = Etudiant() e. [+tab] # ipython }}} ---- == Déployer South dans une app existante == [[http://south.aeracode.org/docs/convertinganapp.html#converting-an-app|Documentation officielle]] * faire un syncdb avant {{{ python manage.py syncdb }}} ''On veut d'abord être sûr que la DB reflète les modèles actuels'' * amorcer le suivi par South sur une seule machine (poste de travail) {{{ python manage.py convert_to_south personnes }}} * pour chaque autres versions '''actuelles''' de la DB (DEV, TEST, PROD...) {{{ python manage.py migrate personnes 0001 --fake }}} * ne s'applique pas si la DB elle-même est versionnée avec les sources (dans Git) * bmo_repertoire.db est suivie dans Git : tout développeur qui pull le commit ayant cette première migration, aura aussi la DB à jour. * pour chaque '''nouvelle''' autre version de la DB (DEV, TEST, PROD) {{{ python manage.py syncdb python manage.py migrate personnes }}} ---- == TUTORIEL Partie 2 : modifications avancées == === Defaults === * default des données existantes, 2 cas possibles : * models.py : déclarer default pour ce champ si default '''existant et futur''' = même valeur * laisser South nous demander la valeur à appliquer sur existant si on n'en veut pas pour les données futures (user va fournir les données) === Uniques === * supportés dans migration normale === Champs ManyToMany === ''Non couvert : car non couvert dans l'atelier Django donc pas besoin de migrer.'' === Champs personnalisés === ''Non couvert : car non couvert dans l'atelier Django donc pas besoin de migrer.'' ---- == TUTORIEL Partie 3 : commandes avancées et migration de données == === Lister les migrations appliquées === {{{ python manage.py migrate --list }}} ''En général, toutes les migrations devraient être appliqués après leur création.'' === Migration de données === ''Non couvert : savoir qu'on peut coder la transformation des données voulues.'' ex.: migrer des champs password : de password "en clair" à password encrypté * créer une migration de données {{{ python manage.py datamigration tutoriel nom_data_migration }}} * coder en Python le traitement voulu : {{{ forwards }}}, {{{ backwords }}} ---- == TUTORIEL Partie 4 : champs personnalisés == ''Non couvert : car non couvert dans l'atelier Django donc pas besoin de migrer.'' ---- == TUTORIEL Partie 5 : équipes et workflow == * si refactoring complet et/ou données pas en PROD : pas migrer, repartir de zéro * pour le reste : migrer === Développeur === * modifier modèles : models.py * créer migration et l'appliquer : schemamigration, migrate === Workflow d'équipe === * migrations sont versionnées dans Git * appliquer migrations {{{ migrate }}} si il y en a une nouvelle après pull * plus simplement : toujours lancer migrations après un pull (au pire fera rien) {{{ python manage.py migrate }}} * conflit possible si 2 développeurs créent différentes migrations en même temps * South lancera avertissement : si tu veux tout appliquer, relance migration avec {{{ --merge }}} * généralement c'est ok si on travaille pas sur même classes (même si c'est le même models.py) * si marche pas : régler le problème manuellement ((SI en central là pour aider) * il faut donc se coordonner... savoir qui travaille sur quoi === Dépendances inter-applications === * South peut gérer les cas de dépendances inter-applications (ex.: créer les dépendances avant les ForeignKeys) ---- == ANNEXE : Tous les cas de migration == === Structure === ==== CRUD : classe = CRUD table ==== * C : créer classe * si app = nouvelle {{{python manage.py schemamigration tutoriel --initial}}} * si app = existante {{{python manage.py schemamigration tutoriel --auto}}} * {{{python manage.py migrate tutoriel}}} * D : supprimer classe * {{{python manage.py schemamigration tutoriel --auto}}} * {{{python manage.py migrate tutoriel}}} * U : modifier classe = créer | supprimer | modifier champ ==== CRUD champ = CRUD colonne (column) ==== * C : créer champ * {{{python manage.py schemamigration tutoriel --auto}}} * {{{python manage.py migrate tutoriel}}} * defaults * D : supprimer champ * {{{python manage.py schemamigration tutoriel --auto}}} * {{{python manage.py migrate tutoriel}}} * U : modifier champ * {{{python manage.py schemamigration tutoriel --auto}}} * {{{python manage.py migrate tutoriel}}} * {{{python manage.py datamigration tutoriel nom_data_migration}}} ??? === Données === {{{ python manage.py datamigration tutoriel nom_data_migration }}} * principe : séparer en autant de migrations de schéma et de données nécessaires * coder à la main la migration de données * migration South de données s'appliquent quand on veut transformer massivement les données (plusieurs rows) * renommer champ? * créer nouveau champ avec null=True * migrer données dans ce nouveau champ * supprimer ancien champ et rendre nouveau null=False (au besoin) * transformer massivement les données d'un champ ==== CRUD données = CRUD rangées (rows) ==== * C : créer données * D : supprimer données * U : modifier données ''Tous ces cas se gèrent avec formulaires si on veut manipuler qu'un seul row'' ---- == CONCLUSION == * commandes * création de migration : * nouvelle app : {{{ python manage.py schemamigration tutoriel --initial }}} * app existantes : {{{ python manage.py schemamigration --auto }}} * appliquer migrations : {{{ python manage.py migrate }}} * penser à sa DB (structure et données) * '''backup''' de la DB si modifications importantes * après un pull : {{{ python manage.py migrate }}} * se coordonner : savoir qui fait quoi ----