Sequelize.js: cómo usar migraciones y sincronización

137

Estoy cerca de tener mi proyecto listo para lanzar. Tengo grandes planes para después del lanzamiento y la estructura de la base de datos va a cambiar: nuevas columnas en las tablas existentes, así como nuevas tablas y nuevas asociaciones con modelos existentes y nuevos.

Todavía no he tocado las migraciones en Sequelize, ya que solo he tenido datos de prueba que no me importa borrar cada vez que cambia la base de datos.

Con ese fin, actualmente estoy ejecutando sync force: truecuando se inicia mi aplicación, si he cambiado las definiciones del modelo. Esto elimina todas las tablas y las hace desde cero. Podría omitir la forceopción de crear solo nuevas tablas. Pero si los existentes han cambiado, esto no es útil.

Entonces, una vez que agrego las migraciones, ¿cómo funcionan las cosas? Obviamente no quiero que se eliminen las tablas existentes (con datos en ellas), así que sync force: trueestá fuera de discusión. En otras aplicaciones que he ayudado a desarrollar (Laravel y otros marcos) como parte del procedimiento de implementación de la aplicación, ejecutamos el comando migrate para ejecutar cualquier migración pendiente. Pero en estas aplicaciones, la primera migración tiene una base de datos esqueleto, con la base de datos en el estado en el que se encontraba en algún momento temprano del desarrollo: la primera versión alfa o lo que sea. Entonces, incluso una instancia de la aplicación que llega tarde a la fiesta puede ponerse al día de una sola vez, ejecutando todas las migraciones en secuencia.

¿Cómo genero tal "primera migración" en Sequelize? Si no tengo una, una nueva instancia de la aplicación en algún momento no tendrá una base de datos esqueleto para ejecutar las migraciones o ejecutará la sincronización al inicio y hará que la base de datos esté en el nuevo estado con todos las nuevas tablas, etc., pero luego, cuando intenta ejecutar las migraciones, no tendrán sentido, ya que se escribieron teniendo en cuenta la base de datos original y cada iteración sucesiva.

Mi proceso de pensamiento: en cada etapa, la base de datos inicial más cada migración en secuencia debe ser igual (más o menos datos) a la base de datos generada cuando sync force: truese corre. Esto se debe a que las descripciones del modelo en el código describen la estructura de la base de datos. Entonces, tal vez si no hay una tabla de migración, simplemente ejecutamos sync y marcamos todas las migraciones como hechas, aunque no se ejecutaron. ¿Es esto lo que tengo que hacer (¿cómo?), O Sequelize se supone que debe hacer esto por sí mismo, o estoy ladrando el árbol equivocado? Y si estoy en el área correcta, seguramente debería haber una buena manera de generar automáticamente la mayor parte de una migración, dados los viejos modelos (¿por hash de compromiso? ¿O podría cada migración estar vinculada a un compromiso? Reconozco que estoy pensando en un universo centrado en git no portátil) y los nuevos modelos. Puede diferenciar la estructura y generar los comandos necesarios para transformar la base de datos de lo antiguo a lo nuevo y viceversa, y luego el desarrollador puede entrar y hacer los ajustes necesarios (eliminar / cambiar datos particulares, etc.).

Cuando ejecuto la secuencia binaria con el --initcomando, me da un directorio de migraciones vacío. Cuando lo ejecuto sequelize --migrate, me convierte en una tabla SequelizeMeta sin nada, ninguna otra tabla. Obviamente no, porque ese binario no sabe cómo iniciar mi aplicación y cargar los modelos.

Debo estar perdiendo algo.

TLDR: ¿cómo configuro mi aplicación y sus migraciones para que se puedan actualizar varias instancias de la aplicación en vivo, así como una nueva aplicación sin base de datos de inicio heredada?

tembloroso
fuente
2
He respondido sobre su flujo de trabajo, pero idealmente todas las tablas deben configurarse usando migraciones. Incluso si está usando syncpor ahora, la idea es que las migraciones "generen" toda la base de datos, por lo que confiar en un esqueleto es en sí mismo un problema. El flujo de trabajo de Ruby on Rails, por ejemplo, usa Migraciones para todo, y es bastante impresionante una vez que te acostumbras. Editar: Y sí, me di cuenta de que esta pregunta es bastante antigua, pero dado que nunca ha habido una respuesta satisfactoria y la gente puede venir aquí en busca de orientación, pensé que debería contribuir.
Fernando Cordeiro

Respuestas:

88

Generando la "primera migración"

En su caso, la forma más confiable es hacerlo casi manualmente. Sugeriría utilizar la herramienta sequelize-cli . La sintaxis es bastante simple:

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

Esto creará tanto el modelo como la migración. Luego, combine manualmente sus modelos existentes con los generados con sequelize-cli, y haga lo mismo con las migraciones. Después de hacer esto, limpie la base de datos (si es posible) y ejecute

sequelize db:migrate

Esto creará un esquema de migraciones. Debe hacer esto solo una vez para cambiar al proceso adecuado de desarrollo de esquemas (sin sincronización: fuerza, pero con migraciones autorizadas).

Más tarde, cuando necesite cambiar el esquema:

  1. Crea una migración: sequelize migration:create
  2. Escriba funciones arriba y abajo en su archivo de migración
  3. De acuerdo con sus cambios en el archivo de migración, cambie su modelo manualmente
  4. correr sequelize db:migrate

Ejecución de migraciones en producción

Obviamente, no puede enviar ssh al servidor de producción y ejecutar migraciones manualmente. Use umzug , herramienta de migración agnóstica de marco para Node.JS para realizar migraciones pendientes antes de que comience la aplicación.

Puede obtener una lista de migraciones pendientes / aún no ejecutadas como esta:

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

Luego ejecute migraciones ( dentro de la devolución de llamada ). El método de ejecución es una función de propósito general que ejecuta para cada migración especificada la función respectiva:

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

Y mi sugerencia es hacerlo antes de que la aplicación se inicie e intente servir rutas cada vez. Algo como esto:

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

No puedo probar esto ahora, pero a primera vista debería funcionar.

UPD Abr.2016

Después de un año, sigue siendo útil, así que comparto mis consejos actuales. Por ahora, estoy instalando el sequelize-clipaquete según la dependencia en vivo requerida , y luego modifico los scripts de inicio de NPM de package.jsonesta manera:

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

Lo único que necesito hacer en el servidor de producción es npm start. Este comando ejecutará todas las migraciones, aplicará todas las sembradoras e iniciará el servidor de aplicaciones. No es necesario llamar a umzug manualmente.

f1nn
fuente
3
Esto suena como lo que estoy buscando. No parece tan mágico y automático como "debería" ser, pero tal vez sea lo mejor que se puede esperar. Sin embargo, actualmente no estoy trabajando con Sequelize y no podré probar esto pronto. Pero si alguien más está de acuerdo en que esta solución es buena, aceptaré esta respuesta. Todavía me parece un poco triste que parece que no hay forma de hacer automáticamente estas migraciones desde las diferencias entre las versiones del modelo.
temblorosa
44
@tremby el único marco que he usado que realmente entiende los modelos fue Django. Analiza modelos y pregunta como "Bueno, parece que cambiaste el nombre del campo a first_name en el modelo de usuario. ¿Deseas crear una migración para él?" En Django funciona casi mágicamente, otras herramientas que he usado asumen el mismo enfoque de migraciones que mencioné anteriormente: usted es responsable de escribir las migraciones usted mismo, entendiendo profundamente qué campo de qué tipo agregar para que sea real a sus estados de modelo actuales
f1nn
2
Puede deshacerse de pendingy luego executey simplemente hacer umzug.up().then(function (migrations) { app.listen(3000); }). Según la documentación de umzug, esto ejecutará todas las migraciones pendientes.
Vinay
Cuando completa la migración, ¿es común agregar los campos al esquema en el archivo del modelo original?
theptrk
@ f1nn Tengo una pregunta sobre su configuración, ¿cómo maneja la agrupación y disponibilidad de aplicaciones? Integraré pm2 en mi flujo de trabajo y tal vez no funcione directamente con los scripts npm.
diosney
17

Solo aprendí esto yo mismo, pero creo que recomendaría usar migraciones ahora para que te acostumbres. He descubierto que lo mejor para descubrir lo que sucede en la migración es mirar el sql en las tablas creadas por sequelize.sync()y luego construir las migraciones desde allí.

migrations -c [migration name] 

Creará el archivo de migración de plantilla en un directorio de migraciones. Luego puede llenarlo con los campos que necesita crear. Este archivo deberá incluir createdAt/ updatedAt, campos necesarios para asociaciones, etc.

Para la creación de la tabla inicial, debería tener:

migration.dropTable('MyTable');

Pero las actualizaciones posteriores a la estructura de la tabla pueden dejar esto fuera y solo usar alter table.

./node_modules/.bin/sequelize --migrate

Un ejemplo de creación se vería así:

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

Para rehacer desde el principio:

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

Estoy usando café para ejecutar un archivo semilla para llenar las tablas después de:

coffee server/seed.coffee

Esto solo tiene una función de creación que se parece a:

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'
    ]
  ]

Recuerde quitar su sync()índice de sus modelos o sobrescribirá lo que hacen las migraciones y las semillas.

Los documentos están en http://sequelize.readthedocs.org/en/latest/docs/migrations/, por supuesto. Pero la respuesta básica es que debe agregar todo en usted para especificar los campos que necesita. No lo hace por ti.

usuario1916988
fuente
55
No estaba preguntando cómo crear y ejecutar migraciones, como usted señaló, todo eso está disponible en la documentación. Lo que estaba preguntando es cómo usarlos en el contexto de una aplicación reutilizable donde las instancias existentes deben actualizarse a una versión de base de datos más nueva y las nuevas instancias necesitan que la base de datos se haga desde cero. O quizás usted está respondiendo a eso, y diciendo que yo no debería estar usando sincronización () en absoluto, y haciendo que la base de datos inicial y todos los cambios que en las migraciones. ¿Eso es lo que estás diciendo?
temblorosa
1
@tremby Creo que eso es lo que está diciendo. Puede usar la sincronización y manejar los resultados, o puede crear todas las migraciones manualmente. Nuestros marcos, al estilo de Rails, generan archivos de migración basados ​​en un esquema de diferencias, me ENCANTARÍA si Sequelize lo hiciera por mí. Demasiado doloroso para hacer migraciones manualmente ...
enviado el
Es una pena que no pueda sequelize.sync()tener un script generado que cree todas las tablas e índices básicos como su primera migración (similar a los rieles schema.rb). Después de leer esto, parece que su mejor opción podría ser exportar su esquema inicial como sql, luego póngalo en una gran execdeclaración en su primera migración. Luego, desde allí, está ejecutando cambios incrementales en un punto de partida conocido como "versión 1.0".
thom_nic
11

Para el desarrollo , ahora hay una opción para sincronizar las tablas actuales alterando su estructura. Usando la última versión del repositorio github de la secuencia , ahora puede ejecutar la sincronización con el alterparámetro.

Table.sync({alter: true})

Una advertencia de los documentos:

Altera las mesas para adaptarse a los modelos. No recomendado para uso en producción. Elimina datos en columnas que fueron eliminadas o que su tipo cambió en el modelo.

meyer9
fuente
3

Ahora con la nueva secuencia de migración, es muy simple.

Este es un ejemplo de lo que puedes hacer.

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

Recuerda que debes configurar:

"dialectOptions": { "multipleStatements": true }

en la configuración de la base de datos.

Nestor Magalhaes
fuente
¿No se cae y se vuelve a crear la base de datos?
TWilly
Creo que usar un archivo sql grande inicial no es la forma recomendada de hacerlo, ya que vinculará el adaptador y la base de datos, que de otro modo sería independiente de la base de datos, ya que puede usarlo para el desarrollo de sqlite y para la producción de mariadb u otros.
diosney
2

Usar versión La versión de la aplicación depende de la versión de la base de datos. Si la nueva versión requiere una actualización de una base de datos, cree una migración para ella.

actualización: decidí abandonar la migración ( KISS ) y ejecutar el script update_db (sync forse: false) cuando sea necesario.

Sergey Karasev
fuente
De manera similar a mi respuesta a la respuesta del usuario 1916988, ¿está diciendo que no debería usar sync()nada y que necesito escribir manualmente las migraciones del esquema de los modelos de la versión anterior a los modelos de la versión más nueva?
temblor
I +1 ed debido a su actualización. Realmente estoy pensando en hacer lo mismo. Escribir todas las migraciones manualmente cuando la aplicación puede hacer eso es un poco estúpido, así que solo voy a hacer un script manual que ejecute la aplicación una vez y ejecute la función de sincronización.
Sallar
2

Un poco tarde, y después de leer la documentación, no necesita tener esa primera migración de la que está hablando. Todo lo que tiene que hacer es llamar syncpara crear las tablas.

sequelize.sync()

También puede ejecutar una sincronización de modelo simple haciendo algo como:

Project.sync()pero creo que sequelize.sync()es un caso general más útil para su proyecto (siempre que importe los buenos modelos en el momento de inicio).

(tomado de http://sequelizejs.com/docs/latest/models#database-synchronization )

Esto creará todas las estructuras iniciales . Luego, solo tendrá que crear migraciones para evolucionar sus esquemas.

Espero eso ayude.

kiddouk
fuente
77
No creo que leas la publicación original muy a fondo, o tal vez no fui lo suficientemente claro. Soy más que consciente de sequelize.sync()lo que hace.
tembloroso
2

Sequelize puede ejecutar SQL arbitrario de forma asincrónica .

Lo que haría es:

  • Generar una migración (para usar como primera migración);
  • Volcar su base de datos, algo como: mysql_dump -uUSER -pPASS DBNAME > FILE.SQL
  • Pegue el volcado completo como texto (peligroso) o cargue un archivo con el volcado completo en el 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');
  • Ejecute este volcado en Sequelize Migration:
module.exports = {
  up: function (migration, DataTypes) {
    var baseSQL = "whatever" // I recommend loading a file
    migration.migrator.sequelize.query(baseSQL);
  }
}

Eso debería encargarse de configurar la base de datos, aunque lo asíncrono puede convertirse en un problema. Si eso sucede, buscaría una forma de diferir la devolución de la upfunción de secuencia hasta que finalice la función asíncrona query.

Más sobre mysql_dump: http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html
Más sobre Sequelize Migrations: http://sequelize.readthedocs.org/en/latest/docs/migrations/
Más sobre Ejecutar SQL desde Sequelize Migration: https://github.com/sequelize/sequelize/issues/313

Fernando Cordeiro
fuente
1

Aquí está mi flujo de trabajo actual. Estoy abierto a sugerencias.

  1. Establezca la secuencia para crear tablas que no existen
  2. Configure la secuencia para soltar y volver a crear todas las tablas en una base de datos en blanco llamada _blank
  3. Use una herramienta mysql para comparar _blank y sincronizar los cambios con esa herramienta. Todavía estoy buscando una herramienta asequible que pueda hacer esto en Mac. Parece que MySql workbench puede importar un modelo desde un esquema existente y luego sincronizarlo. Intentando descubrir cómo hacerlo a través de la línea de comandos para hacerlo más fácil.

De esa manera, no tiene que actualizar manualmente la tabla de migraciones y tener que preocuparse por los dedos gordos, pero aún así obtiene un ORM.

TWilly
fuente
1

Amigo, tuve la misma pregunta y logré entender cómo usarlos.

Comencé sin la secuencia ORM, por lo tanto, ya tenía un modelo de datos.
Tuve que generar los modelos automáticamente con sequelize-auto y generar sus migraciones con este archivo que creas https://gist.github.com/ahelord/a7a7d293695b71aadf04157f0f7dee64 y lo sincronizo ( {Force: false})
Esto está en dev.Tendría que versión el modelo y las migraciones y las ejecuto cada vez que extraigo el código.

En producción, el servidor solo está arriba, por lo que solo tiene que ejecutar migraciones y en cada gestión de confirmación, ya que versionará el modelo sin detener el backend

Leonardo Rodriguez
fuente
1

Revisé esta publicación y preguntas similares, realmente no me respondió. Las migraciones son útiles para activar bases de datos locales y para actualizar datos en producción

Hice la pregunta aquí y también la respondí: ¿ Flujo de trabajo para manejar las migraciones e inicializaciones?

Versión TL-DR para un proyecto greenfield

  1. Diseñe su esquema de base de datos como lo haría tradicionalmente usando scripts SQL puros o si usa una herramienta gui en su lugar
  2. Cuando finalice todo el 95% de su esquema de base de datos y esté satisfecho con él, continúe y muévalo a la secuencia moviendo todo el .sqlarchivo
  3. Haz tu primera migración. Ejecuta sequelize init:migrateen la carpeta donde modelsestés
  4. Haz tu primer archivo de migraciones. corrersequelize migration:generate --name [name_of_your_migration]
  5. En ese archivo de migración, coloque este código allí
("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;
`),
};

ingrese la descripción de la imagen aquí

con esta estructura general de carpetas

ingrese la descripción de la imagen aquí

  1. Ahora su configuración de secuencia se sincroniza con su esquema de base de datos inicial
  2. cuando desee editar el esquema de su base de datos, vuelva a ejecutar esto sequelize migration:generate --name [name_of_your_migration]
  3. Seguir adelante y hacer las modificaciones aquí en los upy downlas vías de migración. Estas son sus declaraciones ALTER para cambiar los nombres de las columnas, ELIMINAR, AGREGAR columnas, etc.
  4. correr sequelize db:migrate
  5. Desea que los modelos se sincronicen con los cambios en su base de datos remota, por lo que ahora puede hacerlo npm install sequelize-auto.
  6. Esto leerá el esquema de la base de datos actual en su base de datos y generará automáticamente archivos de modelo. Use un comando similar a este que se sequelize-auto -o "./models" -d sequelize_auto_test -h localhost -u my_username -p 5432 -x my_password -e postgresencuentra en https://github.com/sequelize/sequelize-auto

Puede usar git para ver los archivos de registro en su modelo, solo debe haber cambios que reflejen los cambios en el modelo de base de datos. Como nota al margen, nunca modifique modelsdirectamente si lo usa sequelize auto, ya que esto los generará para usted. Del mismo modo, ya no debe modificar su esquema de base de datos directamente con archivos SQL, dado que esta es una opción ya que puede importarlos.sql archivos

Ahora el esquema de su base de datos está actualizado y se ha mudado oficialmente para secuenciar solo las migraciones de la base de datos.

Todo está controlado por la versión. Este es el flujo de trabajo ideal para desarrolladores de bases de datos y backend

Vincent Tang
fuente
0

Hay una forma aún más simple (evitando Sequalize). Lo que va así:

  1. Escribe un comando dentro de su proyecto: npm run migrate: new

  2. Esto crea 3 archivos. Un archivo js y dos archivos sql nombrados arriba y abajo

  3. Pones tu declaración SQL en esos archivos, que es puro sql
  4. Luego escribe: npm run migrate: up o npm run migrate: down

Para que esto funcione, eche un vistazo al módulo db-migrate .

Una vez que lo configura (lo que no es difícil), cambiar su base de datos es realmente fácil y ahorra mucho tiempo.

Vedran Maricevic.
fuente