Sequelize.js: come utilizzare le migrazioni e la sincronizzazione


137

Sono vicino ad avere il mio progetto pronto per il lancio. Ho grandi progetti per il dopo lancio e la struttura del database cambierà: nuove colonne nelle tabelle esistenti, nonché nuove tabelle e nuove associazioni a modelli esistenti e nuovi.

Non ho ancora toccato le migrazioni in Sequelize, dal momento che ho avuto solo dati di test che non mi dispiace cancellare ogni volta che il database cambia.

A tal fine, al momento sono in esecuzione sync force: trueall'avvio della mia app, se ho modificato le definizioni del modello. Questo elimina tutti i tavoli e li rende da zero. Potrei omettere l' forceopzione per farlo solo creare nuove tabelle. Ma se quelli esistenti sono cambiati, questo non è utile.

Quindi, una volta aggiunto nelle migrazioni, come funzionano le cose? Ovviamente non voglio che le tabelle esistenti (con dati al loro interno) vengano cancellate, quindi sync force: trueè fuori discussione. Su altre app che ho aiutato a sviluppare (Laravel e altri framework) come parte della procedura di distribuzione dell'app, eseguiamo il comando migrate per eseguire eventuali migrazioni in sospeso. Ma in queste app la prima migrazione ha un database scheletro, con il database nello stato in cui era un po 'di tempo all'inizio dello sviluppo: la prima versione alpha o altro. Quindi anche un'istanza dell'app in ritardo alla festa può essere aggiornata in una sola volta, eseguendo tutte le migrazioni in sequenza.

Come posso generare una "prima migrazione" in Sequelize? Se non ne ho uno, una nuova istanza dell'app in qualche modo lungo la linea non avrà un database scheletro su cui eseguire le migrazioni o eseguirà la sincronizzazione all'inizio e renderà il database nel nuovo stato con tutti le nuove tabelle ecc., ma quando tentano di eseguire le migrazioni non avranno senso, dal momento che sono state scritte con il database originale e ogni successiva iterazione in mente.

Il mio processo di pensiero: in ogni fase, il database iniziale più ogni migrazione in sequenza dovrebbe essere uguale (più o meno dati) al database generato quando sync force: trueè eseguito. Questo perché le descrizioni dei modelli nel codice descrivono la struttura del database. Quindi, forse, se non esiste una tabella di migrazione, eseguiamo solo la sincronizzazione e contrassegniamo tutte le migrazioni come completate, anche se non sono state eseguite. È questo ciò che devo fare (come?) O Sequelize dovrebbe farlo da solo, o sto abbaiando sull'albero sbagliato? E se sono nella giusta area, sicuramente dovrebbe esserci un bel modo per generare automaticamente la maggior parte di una migrazione, dati i vecchi modelli (con hash di commit? O anche ogni migrazione potrebbe essere legata a un commit? Ammetto che sto pensando in un universo git-centrico non portatile) e i nuovi modelli. Può differenziare la struttura e generare i comandi necessari per trasformare il database da vecchio a nuovo e viceversa, quindi lo sviluppatore può accedere e apportare tutte le modifiche necessarie (eliminazione / transizione di dati particolari ecc.).

Quando eseguo il binario sequelize con il --initcomando mi dà una directory di migrazioni vuota. Quando sequelize --migratelo eseguo, mi viene in mente una tabella SequelizeMeta che non contiene nulla, né altre tabelle. Ovviamente no, perché quel binario non sa come avviare la mia app e caricare i modelli.

Mi manca qualcosa.

TLDR: come si configura la mia app e le sue migrazioni in modo da poter aggiornare varie istanze dell'app live, nonché una nuovissima app senza database di avvio legacy?


2
Ho risposto al tuo flusso di lavoro, ma idealmente tutte le tabelle dovrebbero essere configurate usando le migrazioni. Anche se stai usando syncper ora, l'idea è che le migrazioni "generano" l'intero database, quindi fare affidamento su uno scheletro è di per sé un problema. Il flusso di lavoro di Ruby on Rails, ad esempio, utilizza le migrazioni per tutto ed è abbastanza fantastico una volta che ti ci abitui. Modifica: E sì, ho notato che questa domanda è piuttosto vecchia, ma visto che non c'è mai stata una risposta soddisfacente e le persone possono venire qui in cerca di guida, ho pensato che avrei dovuto contribuire.
Fernando Cordeiro,

Risposte:


88

Generazione della "prima migrazione"

Nel tuo caso, il modo più affidabile è farlo quasi manualmente. Suggerirei di usare lo strumento sequelize-cli . La sintassi è piuttosto semplice:

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

Ciò creerà sia modello che migrazione. Quindi, unisci manualmente i tuoi modelli esistenti con quelli generati con sequelize-cli e fai lo stesso con le migrazioni. Dopo aver fatto ciò, cancellare il database (se possibile) ed eseguire

sequelize db:migrate

Questo creerà le migrazioni dello schema. Dovresti farlo solo una volta per passare al corretto processo di sviluppo dello schema (senza sincronizzazione: forza, ma con migrazioni autorevoli).

Successivamente, quando è necessario modificare lo schema:

  1. Crea una migrazione: sequelize migration:create
  2. Scrivi le funzioni su e giù nel tuo file di migrazione
  3. In base alle modifiche apportate al file di migrazione, modificare manualmente il modello
  4. Correre sequelize db:migrate

Esecuzione di migrazioni sulla produzione

Ovviamente non è possibile eseguire SSH sul server di produzione ed eseguire manualmente le migrazioni. Usa umzug , strumento di migrazione agnostica del framework per Node.JS per eseguire migrazioni in sospeso prima dell'avvio dell'app.

Puoi ottenere un elenco di migrazioni in sospeso / non ancora eseguite come questa:

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

Quindi eseguire le migrazioni ( all'interno del callback ). Il metodo execute è una funzione generica che esegue per ogni specifica migrazione la rispettiva funzione:

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

E il mio suggerimento è di farlo prima che l'app inizi e cerchi di servire i percorsi ogni volta. Qualcosa come questo:

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
    });
});

Non posso provarlo adesso, ma a prima vista dovrebbe funzionare.

UPD aprile 2016

Dopo un anno, ancora utile, quindi condivido i miei consigli attuali. Per ora, sto installando il sequelize-clipacchetto come dipendenza live richiesta e quindi modifico gli script di avvio di NPM in package.jsonquesto modo:

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

L'unica cosa che devo fare sul server di produzione è npm start. Questo comando eseguirà tutte le migrazioni, applicherà tutti i seeder e avvierà il server delle app. Non è necessario chiamare umzug manualmente.


3
Sembra quello che sto cercando. Non sembra magico e automatico come dovrebbe "essere", ma forse questo è il migliore che si possa sperare. Tuttavia, attualmente non sto lavorando con Sequelize e non potrò testarlo presto. Ma se qualcun altro concorda sul fatto che questa soluzione è buona, accetterò questa risposta. Trovo ancora un po 'triste il fatto che non ci sia modo di effettuare automaticamente queste migrazioni dalle differenze tra le versioni del modello.
tremby,

4
@tremby l'unico framework che ho usato per capire davvero i modelli era Django. Analizza i modelli e chiede "Bene, sembra che tu abbia rinominato il nome del campo in first_name nel modello User. Desideri creare una migrazione per questo?" In Django funziona in modo quasi magico, altri strumenti che ho usato presumono lo stesso approccio sulle migrazioni che ho menzionato sopra: sei responsabile di scrivere tu stesso le migrazioni, comprendendo profondamente quale campo di quale tipo aggiungere per essere effettivo ai tuoi attuali stati modello
f1nn

2
Puoi sbarazzartene pendinge poi executee basta umzug.up().then(function (migrations) { app.listen(3000); }). Secondo la documentazione di umzug, questo eseguirà tutte le migrazioni in sospeso.
Vinay,

Quando si completa la migrazione, è comune aggiungere i campi allo schema nel file del modello originale?
theptrk,

@ f1nn Ho una domanda sulla tua configurazione, come gestisci il clustering delle app e la disponibilità? Integrerò pm2 nel mio flusso di lavoro e forse non funzionerà in modo diretto con gli script npm.
Diosney,

17

Lo sto imparando da solo, ma penso che consiglierei di usare le migrazioni ora in modo da abituarti a loro. Ho trovato la cosa migliore per capire cosa succede nella migrazione è guardare sql sulle tabelle create da sequelize.sync()e quindi costruire le migrazioni da lì.

migrations -c [migration name] 

Creerà il file di migrazione del modello in una directory delle migrazioni. Puoi quindi popolarlo con i campi che devi creare. Questo file dovrà includere createdAt/ updatedAt, campi necessari per le associazioni, ecc.

Per la creazione della tabella iniziale verso il basso dovrebbe avere:

migration.dropTable('MyTable');

Ma i successivi aggiornamenti alla struttura della tabella possono lasciarlo fuori e usare solo alter table.

./node_modules/.bin/sequelize --migrate

Un esempio di creazione sarebbe simile a:

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();
  }

Per ripetere dall'inizio:

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

Sto usando il caffè per eseguire un file seme per popolare le tabelle dopo:

coffee server/seed.coffee

Questo ha solo una funzione di creazione che assomiglia a:

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

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

Ricorda di eliminare l' sync()indice nei tuoi modelli o sovrascriverà ciò che fanno le migrazioni e il seme.

I documenti sono su http://sequelize.readthedocs.org/en/latest/docs/migrations/ ovviamente. Ma la risposta di base è che devi aggiungere tutto in te per specificare i campi di cui hai bisogno. Non lo fa per te.


5
Non stavo chiedendo come creare ed eseguire le migrazioni: come hai sottolineato, è tutto disponibile nella documentazione. Quello che stavo chiedendo è come usarli nel contesto di un'applicazione riutilizzabile in cui le istanze esistenti devono essere aggiornate a una versione più recente del database e le nuove istanze richiedono che il database sia stato creato da zero. O forse si sta rispondendo che, e dicendo che non dovrei essere usando sync () a tutti, e rendendo il database iniziale e tutte le modifiche ad esso in migrazioni. È quello che stai dicendo?
tremby

1
@tremby Penso che sia quello che sta dicendo. Puoi utilizzare la sincronizzazione e gestire i risultati oppure puoi creare le migrazioni tutte manualmente. I nostri framework, alla maniera di Rails, generano file di migrazione basati su uno schema diff, mi piacerebbe che Sequelize lo facesse per me. Troppo dolore per effettuare manualmente le migrazioni ...
dato il via al

È un peccato che non sia sequelize.sync()quindi possibile avere uno script generato che crei tutte le tabelle e gli indici di base come prima migrazione (simile alle rotaie schema.rb). Dopo aver letto questo, sembra che la soluzione migliore potrebbe essere quella di esportare lo schema iniziale come sql, quindi inseriscilo in una grande execdichiarazione nella tua prima migrazione. Quindi da lì stai eseguendo modifiche incrementali rispetto a un punto di partenza noto "versione 1.0".
thom_nic,

11

Per lo sviluppo , ora esiste un'opzione per sincronizzare le tabelle correnti modificandone la struttura. Utilizzando l'ultima versione del repository github sequelize , è ora possibile eseguire la sincronizzazione con il alterparametro.

Table.sync({alter: true})

Un avvertimento dai documenti:

Modifica le tabelle per adattarle ai modelli. Non raccomandato per l'uso in produzione. Elimina i dati nelle colonne che sono state rimosse o il cui tipo è stato modificato nel modello.


3

Ora con la nuova migrazione sequelize è molto semplice.

Questo è un esempio di ciò che puoi fare.

    '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);
                });
        }
    };

Ricorda che devi impostare:

"dialectOptions": { "multipleStatements": true }

sulla configurazione del database.


Questo non fa semplicemente cadere e ricreare il database?
TWilly,

Penso che l'utilizzo di un file sql iniziale di grandi dimensioni non sia il modo consigliato per farlo, poiché legherà l'adattatore e il database, che altrimenti saranno indipendenti dal database, poiché è possibile utilizzare per lo sviluppo di sqlite e per la produzione mariadb o altro.
Diosney,

2

Usa versione. La versione dell'applicazione dipende dalla versione del database. Se la nuova versione richiede un aggiornamento di un database, creare la migrazione per esso.

aggiornamento: ho deciso di abbandonare la migrazione ( KISS ) ed eseguire lo script update_db (sync forse: false) quando è necessario.


Simile alla mia risposta alla risposta dell'utente 1916988, stai dicendo che non dovrei usare sync()affatto e che devo scrivere manualmente le migrazioni dallo schema dei modelli della versione precedente ai modelli della versione più recente?
tremby,

Ho fatto +1 per via del tuo aggiornamento. In realtà sto pensando di fare lo stesso. Scrivere manualmente tutte le migrazioni quando l'app può farlo è un po 'stupido, quindi creerò uno script manuale che esegue l'app una volta ed esegue la funzione di sincronizzazione.
Sallar,

2

Un po 'in ritardo, e dopo aver letto la documentazione, non è necessario avere quella prima migrazione di cui stai parlando. Tutto quello che devi fare è chiamare syncper creare le tabelle.

sequelize.sync()

Puoi anche eseguire una semplice sincronizzazione del modello facendo qualcosa del tipo:

Project.sync()ma penso che sequelize.sync()sia un caso generale più utile per il tuo progetto (a patto che importi i buoni modelli all'inizio).

(tratto da http://sequelizejs.com/docs/latest/models#database-synchronization )

Questo creerà tutte le strutture iniziali . Successivamente, dovrai solo creare migrazioni per evolvere i tuoi schemi.

spero che sia d'aiuto.


7
Non penso che tu abbia letto il post originale molto attentamente, o forse non ero abbastanza chiaro. Sono più che consapevole di sequelize.sync()ciò che fa.
tremby,

2

Sequelize può eseguire SQL arbitrario in modo asincrono .

Quello che vorrei fare è:

  • Genera una migrazione (da utilizzare come prima migrazione);
  • Scarica il tuo database, qualcosa del tipo: mysql_dump -uUSER -pPASS DBNAME > FILE.SQL
  • O incollare il dump completo come testo (pericoloso) o caricare un file con il dump completo in Nodo:
    • var baseSQL = "LOTS OF SQL and it's EVIL because you gotta put \ backslashes before line breakes and \"quotes\" and/or sum" + " one string for each line, or everything will break";
    • var baseSQL = fs.readFileSync('../seed/baseDump.sql');
  • Esegui questo dump su Sequelize Migration:
module.exports = {
  up: function (migration, DataTypes) {
    var baseSQL = "whatever" // I recommend loading a file
    migration.migrator.sequelize.query(baseSQL);
  }
}

Ciò dovrebbe occuparsi della creazione del database, anche se la cosa asincrona potrebbe diventare un problema. Se ciò dovesse accadere, cercherei un modo per rinviare il ritorno della upfunzione sequelize fino all'asincronoquery funzione .

Ulteriori informazioni su mysql_dump: http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html
Ulteriori informazioni su Sequelize Migrations: http://sequelize.readthedocs.org/en/latest/docs/migrations/
Ulteriori informazioni su Esecuzione di SQL da Sequelize Migration: https://github.com/sequelize/sequelize/issues/313


1

Ecco il mio flusso di lavoro attuale. Sono aperto ai suggerimenti.

  1. Imposta sequelize per creare tabelle che non esistono
  2. Imposta sequelize per eliminare e ricreare tutte le tabelle in un database vuoto chiamato _blank
  3. Utilizzare uno strumento mysql per confrontare _blank e sincronizzare le modifiche utilizzando quello strumento. Stai ancora cercando uno strumento economico che possa farlo su Mac. MySql workbench sembra che sia possibile importare un modello da uno schema esistente e quindi sincronizzare lo schema. Prova a capire come farlo tramite la riga di comando per renderlo semplice.

In questo modo non è necessario aggiornare manualmente la tabella delle migrazioni e preoccuparsi delle dita grosse, ma si ottiene comunque un ORM.


1

Amico, ho avuto la stessa domanda e sono riuscito a capire come usarli.

Ho iniziato senza ORM sequelize quindi avevo già un modello di dati.
Ho dovuto generare i modelli automaticamente con sequelize-auto e generare le loro migrazioni con questo file che hai creato https://gist.github.com/ahelord/a7a7d293695b71aadf04157f0f7dee64 e messo in sync ( {Force: false})
Questo è in dev. Dovrei versione il modello e le migrazioni ed eseguirle ogni volta che estraggo il codice.

In produzione il server è solo al piano di sopra, quindi devi solo eseguire le migrazioni e in ogni commit gestire come farai versione del modello senza arrestare il backend


1

Ho esaminato questo post e domande simili, non mi ha davvero risposto. Le migrazioni sono utili per la creazione di database locali e per l'aggiornamento dei dati in produzione

Ho posto la domanda qui e ho anche risposto: flusso di lavoro per la gestione delle migrazioni sequelizzate e inizializzazione?

Versione TL-DR per un progetto greenfield

  1. Progetta il tuo schema di database come faresti tradizionalmente usando script SQL puri o se invece usi uno strumento gui
  2. Quando finalizzi tutto il tuo 95% del tuo schema db e ne sei soddisfatto, vai avanti e spostalo per sequelizzare spostando l'intero .sql file sopra
  3. Fai la tua prima migrazione. Esegui sequelize init:migratenella cartella in cui ti trovimodels sono a
  4. Crea il tuo primo file di migrazioni. Correresequelize migration:generate --name [name_of_your_migration]
  5. In quel file di migrazione, inserisci questo codice
("use strict");
/**
 * DROP SCHEMA public CASCADE; CREATE SCHEMA public
 * ^ there's a schema file with all the tables in there. it drops all of that, recreates
 */
const fs = require("fs");
const initialSqlScript = fs.readFileSync("./migrations/sql/Production001.sql", {
  encoding: "utf-8",
});
const db = require("../models");
module.exports = {
  up: () => db.sequelize.query(initialSqlScript),
  down: () =>
    db.sequelize.query(`DROP SCHEMA public CASCADE; CREATE SCHEMA public;
`),
};

inserisci qui la descrizione dell'immagine

con questa struttura di cartelle generale

inserisci qui la descrizione dell'immagine

  1. Ora la configurazione del sequelize è sincronizzata con lo schema iniziale del database
  2. quando si desidera modificare lo schema del database, eseguirlo nuovamente sequelize migration:generate --name [name_of_your_migration]
  3. Andare avanti e fare le modifiche qui sulle upe downle vie di migrazione. Queste sono le istruzioni ALTER per modificare i nomi delle colonne, ELIMINA, AGGIUNGI colonne ecc
  4. Correre sequelize db:migrate
  5. Vuoi che i modelli siano sincronizzati con le modifiche al tuo db remoto, quindi quello che puoi fare ora è npm install sequelize-auto.
  6. Questo leggerà lo schema del database corrente sul tuo database e genererà automaticamente i file del modello. Utilizzare un comando simile a questo sequelize-auto -o "./models" -d sequelize_auto_test -h localhost -u my_username -p 5432 -x my_password -e postgrestrovato in https://github.com/sequelize/sequelize-auto

Puoi usare git per vedere i difflog sul tuo modello, ci dovrebbero essere solo cambiamenti che riflettono cambiamenti nel modello di database. Come nota a margine, non modificare mai modelsdirettamente se si utilizza sequelize auto, in quanto ciò li genererà per te. Allo stesso modo, non è più necessario modificare lo schema del database direttamente con i file SQL, a condizione che questa sia un'opzione in quanto è possibile importarli.sql file

Ora lo schema del tuo database è aggiornato e ti sei spostato ufficialmente per sequelizzare solo le migrazioni del database.

Tutto è controllato in versione. Questo è il flusso di lavoro ideale per lo sviluppatore di database e back-end


0

C'è un modo ancora più semplice (evitando Sequalize). Che va così:

  1. Digiti un comando all'interno del tuo progetto: npm run migrate: new

  2. Questo crea 3 file. Un file js e due file sql denominati su e giù

  3. Metti la tua istruzione SQL in quei file, che è sql puro
  4. Quindi digitare: npm run migrate: up o npm run migrate: down

Affinché funzioni, dai un'occhiata al modulo db-migrate .

Dopo averlo configurato (il che non è difficile), cambiare il tuo DB è davvero facile e fa risparmiare molto tempo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.