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

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 a los 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, en este momento estoy ejecutando sync force: true cuando mi aplicación se inicia, si he cambiado las definiciones del modelo. Esto borra todas las tablas y las hace desde cero. Podría omitir la opción force para que solo cree nuevas tablas. Pero si los existentes han cambiado esto no es útil.

Entonces, una vez que agregué migraciones, ¿cómo funcionan las cosas? Obviamente no quiero que se eliminen las tablas existentes (con datos en ellas), así que sync force: true está 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 migrar para ejecutar cualquier migración pendiente. Pero en estas aplicaciones, la primera migración tiene una base de datos de esqueleto, con la base de datos en el estado en el que se encontraba en una etapa temprana del desarrollo: la primera versión alfa o lo que fuera. Así que 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 esquelética para ejecutar las migraciones, o se ejecutará sincronizada 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 fueron escritas con la base de datos original y con cada iteración sucesiva en mente.

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) que la base de datos generada cuando se ejecuta sync force: true . 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 la sincronización y marcamos todas las migraciones como hechas, aunque no se hayan ejecutado. ¿Es esto lo que debo hacer (¿cómo?), O se supone que Sequelize haga esto por sí mismo, ¿o estoy ladrando al árbol equivocado? Y si estoy en el área correcta, seguramente debería haber una buena forma de generar automáticamente la mayor parte de una migración, dados los modelos antiguos (¿por cometer hash? ¿O incluso podría cada migración estar vinculada a un compromiso? Admito que estoy pensando en un universo no portátil centrado en git) y los nuevos modelos. Puede diferenciar la estructura y generar los comandos necesarios para transformar la base de datos de antigua a nueva y viceversa, y luego el desarrollador puede ingresar y realizar los ajustes necesarios (eliminar / transicionar datos particulares, etc.).

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

Debo estar perdiendo algo.

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

Generando la “primera migración”

En su caso, la forma más confiable es hacerlo casi manualmente. Yo sugeriría usar la herramienta secuelize-cli . La syntax es bastante simple:

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

Esto creará tanto el modelo Y la migración. Luego, fusione manualmente sus modelos existentes con generado 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á esquemas de las migraciones. Debe hacer esto solo una vez para cambiar al proceso adecuado de desarrollo de esquemas (sin sync: force, pero con migraciones autorizadas).

Más tarde, cuando necesite cambiar el esquema:

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

Ejecución de migraciones en producción.

Obviamente, no puede ssh al servidor de producción y ejecutar migraciones manualmente. Use umzug , herramienta de migración independiente del marco para Node.JS para realizar migraciones pendientes antes de que se inicie 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 ejecuta migraciones ( dentro de callback ). El método de ejecución es una función de propósito general que se ejecuta para cada migración especificada en 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 se inicie la aplicación 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 intentar 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 paquete sequelize-cli como dependencia en vivo requerida, y luego modifico los scripts de inicio de NPM en package.json esta 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á todos los sembradores e iniciará el servidor de aplicaciones. No es necesario llamar a umzug manualmente.

Solo estoy aprendiendo esto, pero creo que recomendaría usar migraciones ahora para que te acostumbres a ellas. He encontrado que lo mejor para descubrir qué pasa con la migración es mirar el sql en las tablas creadas por sequelize.sync () y luego construir las migraciones desde allí.

migraciones -c [nombre de la migración]

creará la plantilla de archivo de migración en un directorio de migraciones. A continuación, puede rellenarlo con los campos que necesita crear. Este archivo deberá incluir createdAt / updatedAt, los campos necesarios para las asociaciones, etc. Para la creación inicial de la tabla, debe tener:

migration.dropTable (‘MyTable’);

pero las actualizaciones posteriores de la estructura de la tabla pueden omitir esto y simplemente usar la tabla alterar.

 ./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 inicio:

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

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

 coffee server/seed.coffee 

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

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

Recuerda sacar tu sync () del índice en tus modelos o se 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 debes agregar todo en ti mismo para especificar los campos que necesitas. No lo hace por ti 🙁

Para el desarrollo , ahora hay una opción para sincronizar las tablas actuales modificando su estructura. Usando la última versión del repository github de secuencia , ahora puede ejecutar sync con el parámetro alter .

 Table.sync({alter: true}) 

Una advertencia de los documentos:

Altera las mesas para que se ajusten a los modelos. No recomendado para uso en producción. Elimina los datos de las columnas que se eliminaron o se modificó su tipo en el modelo.

Usa la 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 la migración para ella.

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

Sequelize puede ejecutar arbitrar SQL. Pero ten cuidado porque es asíncrono .

Lo que yo haría es:

  • Generar una migración (Para usar como primera migración);
  • Vuelque 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 asincrónico puede convertirse en un problema. Si eso sucede, buscaría una forma de diferir el retorno de la función de secuenciación up hasta que la función de query asíncrona haya finalizado.

Más información 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 información sobre la ejecución de SQL desde Sequelize Migration: https://github.com/sequelize/sequelize/issues/313

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 tienes que configurar:

"dialectOptions": { "multipleStatements": true }

en la base de datos config.

Un poco tarde, y después de leer la documentación, no necesita tener la primera migración de la que habla. Todo lo que tienes que hacer es llamar a sync para 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 . Después, solo tendrá que crear migraciones para poder evolucionar sus esquemas.

Espero eso ayude.

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

  1. Establecer secuela para crear tablas que no existen
  2. Establezca secuela 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 usando esa herramienta. Sigo buscando una herramienta asequible que pueda hacer esto en mac. El entorno de trabajo de MySql parece que puede importar un modelo desde un esquema existente y luego sincronizar el esquema. Tratar de averiguar 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 tiene que preocuparse por los dedos gruesos, pero sigue obteniendo un ORM.

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

Comencé sin secuela 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 pones en sincronización ( {Force: false} )
Esto está en dev.I tendría que versionar el modelo y las migraciones y ejecutarlas cada vez que tire del código.

En producción, el servidor está solo en la planta superior, por lo que solo tiene que ejecutar migraciones y en cada gestión de confirmación, ya que la versión del modelo se realizará sin detener el servidor.

Hay una manera aún más simple (evitando la Sequalización). Que va así:

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

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

  3. Pones tu statement SQL en esos archivos, que es sql puro
  4. Luego escribe: npm ejecuta migrate: up o npm ejecuta 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 sencillo y ahorra mucho tiempo.