migrations avec alembic ======================= .. warning :: Les migrations ne sont pas appliquées à une nouvelle installation. Ne pas compter dessus pour initialiser la BDD. Pour ça, regarder du côté du `script populate/syncdb`_. .. _script populate/syncdb: https://framagit.org/caerp/caerp/-/blob/master/caerp/models/populate.py Créer des migrations --------------------- .. note :: Il est possible de grouper **schema migration** et **data migration** dans un même fichier de migration si pertinent. Évolution de structure de la BDD (schema migration) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. warning :: La création de nouvelle table est automatique et ne nécessite pas de migration. Dans l'ordre : 1. Modifier les modèles (ex: ajout/retrait/renommage de colones). 2. Créer une migration avec détection auto des modifications .. code-block:: console caerp-migrate development.ini revision --m 'Description de la migration' 3. Faire le ménage dans les changements détectés pour ne garder que ceux pertinents, dans le ``upgrade()`` comme dans le ``downgrade()``… Ou carrément de modifier ce qu'a détecté alembic, car il a ses limites (ex: ne détecte pas le renommage de table). Évolution des données (data migration) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ En général il est plus simple de partir d'une migration vide : .. code-block:: console caerp-migrate development.ini revision --empty --m 'Description de la migration' .. note:: ``op.execute()`` et ``conn.execute()`` permettent d'exécuter du code SQL tous les deux. ``op.execute()`` demande moins de code (on gagne le get_bind) mais ne permet pas de récupérer le résultat de la requête. Pour les ``SELECT`` il faut donc utiliser ``conn.execute`` Quelques conseils pour les **data migrations** : * Même si c'est tentant, ne pas importer les modèles dans les migrations (car aucune garantie de leur stabilité dans le temps), mieux vaut faire du SQL brut. Exception : le modèle ``caerp.models.config.Config`` dont on sait qu'il ne bougera pas trop. * Ne pas oublier ``mark_changed(session)`` et ``session.flush()`` après un ``*.execute()`` sans quoi les modifications opérées dans la migration seront sans effet… * Tenter de limiter le nombre de requêtes (éviter les boucles avec un ``execute()`` à chaque tour). C'est toujours ça de temps de déploiement / mise à jour gagné… Migrations de merge ^^^^^^^^^^^^^^^^^^^ Les revisions forment un graphe, et peuvent, à la manière des historiques git, diverger. Cela arrive typiquement quand on rebase une MR sur le master. Une erreur parlant de **multiple heads** apparaît alors. La CI est configurée pour râler dans ces cas également (`import_reference_dump` va échouer). On peut résoudre ce problème en créant des **révisions de merge** (``caerp-migrate development.ini merge``). Une nouvelle migration qui « réunit » les 2 migrations de merge sera automatiquement créée. Il faut commiter cette migration. Linéariser le graphe de migrations (rebase) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ On peut aussi, pour simplifier l'historique et clarifier l'ordre dans lequel il sera exécuté, linéariser l'historique. La commande ``caerp-rebase`` permet de faire ça. Elle a deux usages détaillés ci-après. .. danger:: À manier avec précaution, il ne faudrait pas qu'un *rebase* de l'historique alembic conduise à « sauter » des migrations sur des données de prod. Le risque est plus élevé qu'avec des *merge* et nécessite de comprendre ce qui a été déployé ou non en prod. .. tip:: **L'ordre compte** Dans le cas d'une merge request qu'on linéarise avant de la merger dans ``master``, il faut bien que ``--rev1`` désigne la révision déjà dans master et ``--rev2`` la dernière révision de la MR. Usage 1 : Supprimer une migration de merge en gardant les deux branches """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" Si on a une révision de merge, on peut la supprimer de l'historique : Exemple, si on a ces 7 migrations : .. code-block:: 8a238e064197 → 7c3bc9b26029: 2024.2.0 Ajout de la catégorie des types de dépenses 7c3bc9b26029 → f57584a7076f: 2024.1.0b1 Ajoute BusinessBPFData.has_remote 7c3bc9b26029 → c7f17be86e32: 2024.2.0 Rend certains champs de BusinessBPFData nullables c7f17be86e32 → a389d617354b: 2024.2.0 Supprime de TrainerData les champs « un petit peu de vous » a389d617354b → e032a4187413: 2024.2.0 Ajout du compte client sur les TVA ('e032a4187413', 'f57584a7076f') → d4d4be3b65a6: 2024.1.0b1 Revision merge d4d4be3b65a6 → 303f11e5dbd4: 2024.1.0b1 Création et déplacement dispositif et administratif Si on lance `caerp-migrate development.ini rebase --rev1 f57584a7076f --rev2 e032a4187413 --revmerge d4d4be3b65a6`, on obtient ces 6 migrations .. code-block:: 8a238e064197 → 7c3bc9b26029: 2024.2.0 Ajout de la catégorie des types de dépenses 7c3bc9b26029 → f57584a7076f: 2024.1.0b1 Ajoute BusinessBPFData.has_remote f57584a7076f → c7f17be86e32: 2024.2.0 Rend certains champs de BusinessBPFData nullables c7f17be86e32 → a389d617354b: 2024.2.0 Supprime de TrainerData les champs « un petit peu de vous » a389d617354b → e032a4187413: 2024.2.0 Ajout du compte client sur les TVA e032a4187413 → 303f11e5dbd4: 2024.1.0b1 Création et déplacement dispositif et administratif Usage 2 : aplatir 2 « heads » """""""""""""""""""""""""""""" Si on a deux « fourches » non encore réunies par une migation de merge, on peut aussi utiliser la commande de la manière suivante : caerp-migrate development.ini rebase --rev1 f57584a7076f --rev2 e032a4187413 .. note:: En cas de doute en contexte de développement sur la cohérence de l'historique en dév, on peut toujours repartir d'une base saine (``caerp-load-demo-data``) Liens ----- * Doc de ``op.execute()`` : https://alembic.sqlalchemy.org/en/latest/ops.html?highlight=execute#alembic.operations.Operations.execute