web-dev-qa-db-fra.com

Sequelize.js: comment utiliser les migrations et la synchronisation

Je suis sur le point de lancer mon projet. J'ai de grands projets pour après le lancement et la structure de la base de données va changer: de nouvelles colonnes dans les tables existantes ainsi que de nouvelles tables et de nouvelles associations avec les modèles existants et nouveaux.

Je n'ai pas encore abordé les migrations dans Sequelize, car je n'ai que des données de test que je ne crains pas d'effacer chaque fois que la base de données change.

À cette fin, je lance actuellement sync force: true au démarrage de mon application, si j'ai modifié les définitions de modèle. Cela supprime toutes les tables et les crée à partir de zéro. Je pourrais omettre l’option force pour qu’elle ne crée que de nouvelles tables. Mais si ceux qui existent ont changé, cela n’est pas utile.

Donc, une fois que j'ai ajouté les migrations, comment ça marche? Évidemment, je ne veux pas que les tables existantes (contenant des données) soient effacées, donc sync force: true est hors de question. Sur d'autres applications que j'ai aidées à développer (Laravel et autres frameworks) dans le cadre de la procédure de déploiement de l'application, nous exécutons la commande migrate pour exécuter les migrations en attente. Mais dans ces applications, la toute première migration comporte une base de données squelette, la base de données se trouvant dans l'état où elle était au début du développement - la première version alpha ou autre. Ainsi, même une instance de l'application tardive peut être mise en service en une fois, en exécutant toutes les migrations en séquence.

Comment générer une telle "première migration" dans Sequelize? Si je n'en ai pas, une nouvelle instance de l'application à un moment donné n'aura pas de base de données squelette sur laquelle les migrations seront exécutées, ou elle sera synchronisée au début et fera en sorte que la base de données soit dans le nouvel état les nouvelles tables, etc., mais quand il essaiera de lancer les migrations, elles n’auront aucun sens, car elles ont été écrites avec la base de données originale et chaque itération successive à l’esprit.

Mon processus de pensée: à chaque étape, la base de données initiale plus chaque migration en séquence devrait être égale (plus ou moins les données) à la base de données générée lorsque sync force: true est exécuté. En effet, les descriptions de modèle dans le code décrivent la structure de la base de données. Donc, peut-être que s'il n'y a pas de table de migration, nous exécutons simplement sync et marquons toutes les migrations comme effectuées, même si elles n'ont pas été exécutées. Est-ce ce que je dois faire (comment?) Ou est-ce que Sequelize est censé le faire lui-même ou est-ce que je me trompe d'arbre? Et si je suis dans le bon domaine, il devrait sûrement y avoir un bon moyen de générer automatiquement la plus grande partie d’une migration, étant donné les anciens modèles (par commit hash? Ou même chaque migration pourrait-elle être liée à un commit? Je concède que je pense dans un univers non-portable git-centric) et les nouveaux modèles. Il peut différencier la structure et générer les commandes nécessaires pour transformer la base de données d’ancienne en nouvelle, et inversement, et ensuite le développeur peut entrer et apporter les modifications nécessaires (suppression/transition de données particulières, etc.).

Lorsque j'exécute le binaire séquentiel avec la commande --init, il me donne un répertoire de migration vide. Quand j’exécute ensuite sequelize --migrate, cela me donne une table SequelizeMeta ne contenant rien, aucune autre table. Évidemment non, car ce binaire ne sait pas comment amorcer mon application et charger les modèles.

J'ai dû louper quelque chose.

TLDR: comment configurer mon application et ses migrations afin de mettre à jour diverses instances de l'application en direct, ainsi qu'une toute nouvelle application sans base de données de démarrage héritée?

114
tremby

Génération de la "première migration"

Dans votre cas, le moyen le plus fiable est de le faire presque manuellement. Je suggérerais d'utiliser sequelize-cli tool. La syntaxe est assez simple:

sequelize init
...
sequelize model:create --name User --attributes first_name:string,last_name:string,bio:text

Cela créera à la fois le modèle ET la migration. Ensuite, fusionnez manuellement vos modèles existants avec générés avec sequelize-cli et procédez de même avec les migrations. Après cela, nettoyez la base de données (si possible) et lancez

sequelize db:migrate

Cela créera des migrations de schéma. Vous ne devez effectuer cette opération qu'une seule fois pour passer au processus de développement du schéma approprié (sans sync: force, mais avec des migrations faisant autorité).

Plus tard, quand vous aurez besoin de changer de schéma:

  1. Créer une migration: sequelize migration:create
  2. Fonctions d'écriture et de révision dans votre fichier de migration
  3. Selon vos modifications dans le fichier de migration, modifiez votre modèle manuellement.
  4. Exécuter sequelize db:migrate

Effectuer des migrations en production

De toute évidence, vous ne pouvez pas passer au serveur de production et exécuter des migrations à la main. Utilisez umzug , outil de migration indépendant de la structure de Node.JS pour effectuer les migrations en attente avant le démarrage de l'application.

Vous pouvez obtenir une liste des migrations en attente/non encore exécutées comme ceci:

umzug.pending().then(function (migrations) {
  // "migrations" will be an Array with the names of
  // pending migrations.
}); 

Puis exécutez les migrations (inside callback). La méthode execute est une fonction à usage général qui exécute pour chaque migration spécifiée la fonction correspondante:

umzug.execute({
  migrations: ['some-id', 'some-other-id'],
  method: 'up'
}).then(function (migrations) {
  // "migrations" will be an Array of all executed/reverted migrations.
});

Et ma suggestion est de le faire avant le démarrage de l'application et d'essayer de desservir les itinéraires à chaque fois. Quelque chose comme ça:

umzug.pending().then(function(migrations) {
    // "migrations" will be an Array with the names of
    // pending migrations.
    umzug.execute({
        migrations: migrations,
        method: 'up'
    }).then(function(migrations) {
        // "migrations" will be an Array of all executed/reverted migrations.
        // start the server
        app.listen(3000);
        // do your stuff
    });
});

Je ne peux pas essayer cela maintenant, mais au premier abord, cela devrait fonctionner.

UPD avril 2016

Après un an, toujours utile, partage donc mes conseils actuels. Pour l'instant, j'installe le paquet sequelize-cli comme requis par la dépendance live, puis je modifie les scripts de démarrage NPM dans package.json comme suit:

...
"scripts": {
  "dev": "grunt && sequelize db:migrate && sequelize db:seed:all && node bin/www",
  "start": "sequelize db:migrate && sequelize db:seed:all && node bin/www"
},
...

La seule chose que je dois faire sur le serveur de production est npm start. Cette commande exécutera toutes les migrations, appliquera tous les générateurs de semences et démarrera le serveur d'applications. Pas besoin d'appeler umzug manuellement.

73
f1nn

Je viens d’apprendre cela moi-même, mais je pense que je recommanderais d’utiliser les migrations maintenant pour que vous vous y habituiez. J'ai trouvé la meilleure chose à faire pour comprendre ce qui se passe dans la migration est de regarder le SQL sur les tables créées par sequelize.sync (), puis de construire les migrations à partir de là. 

migrations -c [nom de la migration] 

créera le fichier de migration de modèle dans un répertoire de migration. Vous pouvez ensuite le renseigner avec les champs dont vous avez besoin. Ce fichier devra inclure createdAt/updatedAt, les champs nécessaires aux associations, etc. Pour la création de la table initiale, down devrait avoir:

migration.dropTable ('MyTable');

mais les mises à jour ultérieures de la structure de la table peuvent laisser cela de côté et utiliser simplement alter table.

./node_modules/.bin/sequelize --migrate

Un exemple de création ressemblerait à ceci:

module.exports = {
  up: function(migration, DataTypes, done) {
    migration.createTable(
        'MyTable',
        {
          id: {
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
          },
          bigString: {type: DataTypes.TEXT, allowNull: false},
          MyOtherTableId: DataTypes.INTEGER,
          createdAt: {
            type: DataTypes.DATE
          },
          updatedAt: {
            type: DataTypes.DATE
          }
        });
    done();
  },
  down: function(migration, DataTypes, done) {
    migration.dropTable('MyTable');
    done();
  }

refaire depuis le début:

./node_modules/.bin/sequelize --migrate --undo
./node_modules/.bin/sequelize --migrate

J'utilise le café pour exécuter un fichier de base pour remplir les tableaux après:

coffee server/seed.coffee

Cela a juste une fonction create qui ressemble à ceci:

user = db.User.create
  username: 'bob'
  password: 'suruncle'
  email: '[email protected]'
.success (user) ->
  console.log 'added user'
  user_id = user.id
  myTable = [
    field1: 'womp'
    field2: 'rat'

    subModel: [
      field1: 'womp'
     ,
      field1: 'rat'
    ]
  ]

N'oubliez pas de sortir votre sync () de l'index dans vos modèles, sinon cela écrasera ce que font les migrations et les valeurs de départ.

Les documents sont à http://sequelize.readthedocs.org/en/latest/docs/migrations/ bien sûr. Mais la réponse de base est que vous devez tout ajouter vous-même pour spécifier les champs dont vous avez besoin. Il ne le fait pas pour vous :-(

15
user1916988

Pour development, il existe maintenant une option pour synchroniser les tables actuelles en modifiant leur structure. En utilisant la dernière version de sequelize github repo , vous pouvez maintenant exécuter la synchronisation avec le paramètre alter.

Table.sync({alter: true})

Un avertissement de la docs:

Modifie les tables en fonction des modèles. Non recommandé pour une utilisation en production. Supprime les données des colonnes supprimées ou dont le type a été modifié dans le modèle.

8
meyer9

Maintenant, avec la nouvelle suite, la migration est très simple.

Ceci est un exemple de ce que vous pouvez faire.

    'use strict';

    var Promise = require('bluebird'),
        fs = require('fs');

    module.exports = {
        up: function (queryInterface, Sequelize) {

            return Promise
                .resolve()
                .then(function() {
                    return fs.readFileSync(__dirname + '/../initial-db.sql', 'utf-8');
                })
                .then(function (initialSchema) {
                    return queryInterface.sequelize.query(initialSchema);
                })
        },

        down: function (queryInterface, Sequelize) {
            return Promise
                .resolve()
                .then(function() {
                    return fs.readFileSync(__dirname + '/../drop-initial-db.sql', 'utf-8');
                })
                .then(function (dropSql) {
                    return queryInterface.sequelize.query(dropSql);
                });
        }
    };

N'oubliez pas que vous devez définir:

"dialectOptions": { "multipleStatements": true }

sur la config de base de données.

2
Nestor Magalhaes

Utiliser la version . La version de l'application dépend de la version de la base de données . Si la nouvelle version nécessite la mise à jour d'une base de données, créez une migration correspondante.

update: j'ai décidé d'abandonner la migration ( KISS ) et d'exécuter le script update_db (sync forse: false) lorsque cela est nécessaire.

2
ahiipsa

Ami, j'ai eu la même question et j'ai réussi à comprendre comment les utiliser. 

J'ai commencé sans ORM, donc j'avais déjà un modèle de données.
J'ai dû générer les modèles automatiquement avec sequelize-auto et générer leurs migrations avec ce fichier que vous créez https://Gist.github.com/ahelord/a7a7d293695b71aadf04157f0f7dee64 et que vous avez synchronisé ({Force: false})
C’est dans dev.J’aurais à mettre à jour le modèle et les migrations et à les exécuter à chaque fois que je tirerais le code. 

En production, le serveur n’est qu’à l’étage; il vous suffit donc d’effectuer des migrations et de gérer dans chaque commit la version du modèle sans arrêter le backend.

0
Leonardo Rodriguez

Il existe un moyen encore plus simple (éviter Sequalize). Qui va comme ça:

  1. Vous tapez une commande dans votre projet: npm run migrate: new

  2. Cela crée 3 fichiers. Un fichier js et deux fichiers SQL nommés de haut en bas

  3. Vous mettez votre instruction SQL dans ces fichiers, qui est pure SQL
  4. Ensuite, vous tapez: npm run migrate: up ou npm run migrate: down

Pour que cela fonctionne, jetez un œil au module db-migrate .

Une fois la configuration terminée (ce qui n’est pas difficile), modifier votre base de données est très simple et vous fait gagner beaucoup de temps.

0

Voici mon flux de travail actuel. Je suis ouvert aux suggestions.

  1. Set séquelize pour créer des tables qui n'existent pas
  2. Définissez sequelizeto pour supprimer et recréer toutes les tables dans une base de données vide appelée _ blank 
  3. Utilisez un outil mysql pour comparer _ vierge et synchroniser les modifications à l’aide de Cet outil. Vous recherchez toujours un outil abordable capable de le faire sur Mac. MySql Workbench semble vous permettre d'importer un modèle à partir d'un schéma existant , Puis de le synchroniser. Essayer de comprendre comment faire cela avec la ligne de commande pour le rendre facile.

De cette façon, vous n'avez pas à mettre à jour manuellement la table des migrations et à vous soucier des gros doigts, mais vous obtenez toujours un ORM.

0
TWilly