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?
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:
sequelize migration:create
sequelize db:migrate
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.
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.
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 :-(
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.
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.
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.
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.
Il existe un moyen encore plus simple (éviter Sequalize). Qui va comme ça:
Vous tapez une commande dans votre projet: npm run migrate: new
Cela crée 3 fichiers. Un fichier js et deux fichiers SQL nommés de haut en bas
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.
Voici mon flux de travail actuel. Je suis ouvert aux suggestions.
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.