Sequelize.js: como usar migrações e sincronização

137

Estou perto de ter meu projeto pronto para lançar. Tenho grandes planos para após o lançamento e a estrutura do banco de dados será alterada - novas colunas nas tabelas existentes, bem como novas tabelas e novas associações aos modelos existentes e novos.

Ainda não toquei em migrações no Sequelize, pois só tive dados de teste que não me importo de eliminar toda vez que o banco de dados muda.

Para esse fim, no momento eu estou executando sync force: truequando meu aplicativo é iniciado, se eu alterei as definições de modelo. Isso exclui todas as tabelas e as cria do zero. Eu poderia omitir a forceopção de criar apenas novas tabelas. Mas se os existentes foram alterados, isso não é útil.

Então, quando adiciono migrações, como as coisas funcionam? Obviamente, não quero que as tabelas existentes (com dados nelas) sejam apagadas, o que sync force: trueestá fora de questão. Em outros aplicativos que ajudei a desenvolver (Laravel e outras estruturas) como parte do procedimento de implantação do aplicativo, executamos o comando migrate para executar qualquer migração pendente. Mas nesses aplicativos, a primeira migração tem um banco de dados esqueleto, com o banco de dados no estado em que estava há algum tempo no início do desenvolvimento - a primeira versão alfa ou o que quer. Assim, mesmo uma instância do aplicativo atrasada para a festa pode acelerar de uma só vez, executando todas as migrações em sequência.

Como faço para gerar essa "primeira migração" no Sequelize? Se eu não tiver uma, uma nova instância do aplicativo mais adiante não terá um banco de dados esqueleto para executar as migrações ou executará a sincronização no início e fará com que o banco de dados fique no novo estado com todos os as novas tabelas, etc., mas quando tentar executar as migrações, elas não farão sentido, pois foram escritas com o banco de dados original e com cada iteração sucessiva em mente.

Meu processo de pensamento: em cada estágio, o banco de dados inicial mais cada migração em sequência deve ser igual (mais ou menos dados) ao banco de dados gerado quando sync force: trueé executado. Isso ocorre porque as descrições do modelo no código descrevem a estrutura do banco de dados. Talvez, se não houver tabela de migração, apenas executemos a sincronização e marcemos todas as migrações como concluídas, mesmo que não tenham sido executadas. É isso que eu preciso fazer (como?), Ou Sequelize deveria fazer isso sozinho, ou estou latindo na árvore errada? E se eu estiver na área certa, certamente deve haver uma boa maneira de gerar automaticamente a maior parte da migração, dados os modelos antigos (por hash de confirmação? Ou até mesmo cada migração pode estar vinculada a uma consolidação? Eu admito que estou pensando em um universo centrado no Git não portátil) e nos novos modelos. Ele pode diferenciar a estrutura e gerar os comandos necessários para transformar o banco de dados de antigo para novo e vice-versa, e então o desenvolvedor pode entrar e fazer os ajustes necessários (exclusão / transição de dados específicos, etc.).

Quando executo o binário sequencializado com o --initcomando, ele me fornece um diretório de migrações vazio. Quando eu executo, sequelize --migrateisso me torna uma tabela SequelizeMeta sem nada, sem outras tabelas. Obviamente não, porque esse binário não sabe como inicializar meu aplicativo e carregar os modelos.

Eu devo estar esquecendo alguma coisa.

TLDR: como configuro meu aplicativo e suas migrações para que várias instâncias do aplicativo ao vivo possam ser atualizadas, bem como um aplicativo totalmente novo sem banco de dados inicial herdado?

tremer
fonte
2
Eu respondi referente ao seu fluxo de trabalho, mas, idealmente, todas as tabelas devem ser configuradas usando migrações. Mesmo se você estiver usando syncpor enquanto, a ideia é que as migrações "gerem" todo o banco de dados, portanto, contar com um esqueleto é um problema. O fluxo de trabalho do Ruby on Rails, por exemplo, usa Migrações para tudo, e é incrível quando você se acostuma. Edit: E sim, notei que esta pergunta é bastante antiga, mas como nunca houve uma resposta satisfatória e as pessoas podem vir aqui procurando orientação, achei que deveria contribuir.
Fernando Cordeiro

Respostas:

88

Gerando a "primeira migração"

No seu caso, a maneira mais confiável é fazê-lo quase manualmente. Eu sugeriria usar a ferramenta sequelize-cli . A sintaxe é bastante simples:

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

Isso criará o modelo e a migração. Em seguida, mescle manualmente seus modelos existentes com os gerados com sequelize-cli e faça o mesmo com migrações. Depois de fazer isso, limpe o banco de dados (se possível) e execute

sequelize db:migrate

Isso criará migrações de esquema. Você deve fazer isso apenas uma vez para alternar para o processo adequado de desenvolvimento de esquema (sem sincronização: force, mas com migrações autorizadas).

Mais tarde, quando você precisar alterar o esquema:

  1. Crie uma migração: sequelize migration:create
  2. Escreva funções para cima e para baixo no seu arquivo de migração
  3. De acordo com suas alterações no arquivo de migração, altere seu modelo manualmente
  4. Corre sequelize db:migrate

Executando Migrações na Produção

Obviamente, você não pode ssh para o servidor de produção e executar migrações manualmente. Use umzug , ferramenta de migração independente de estrutura para o Node.JS para executar migrações pendentes antes do aplicativo ser iniciado.

Você pode obter uma lista de migrações pendentes / ainda não executadas como esta:

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

Em seguida, execute migrações ( retorno de chamada interno ). O método execute é uma função de uso geral que executa para cada migração especificada a respectiva função:

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

E minha sugestão é fazê-lo antes que o aplicativo seja iniciado e tente sempre servir rotas. Algo assim:

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

Não posso tentar isso agora, mas, à primeira vista, deve funcionar.

UPD abr. 2016

Depois de um ano, ainda é útil, então compartilhe minhas dicas atuais. Por enquanto, estou instalando o sequelize-clipacote conforme a dependência ativa necessária e modifique os scripts de inicialização do NPM da package.jsonseguinte maneira:

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

A única coisa que preciso fazer no servidor de produção é npm start. Este comando executará todas as migrações, aplicará todos os semeadores e iniciará o servidor de aplicativos. Não há necessidade de chamar umzug manualmente.

f1nn
fonte
3
Parece o que estou procurando. Não parece tão mágico e automático como "deveria", mas talvez seja o melhor que se pode esperar. No entanto, atualmente não estou trabalhando com o Sequelize e não poderei testá-lo tão cedo. Mas se alguém concordar que esta solução é boa, eu aceito esta resposta. Ainda acho um pouco triste que parece não haver maneira de fazer automaticamente essas migrações a partir das diferenças entre as versões do modelo.
tremby
4
@ tremby o único framework que eu usei que realmente entende modelos foi o Django. Ele analisa modelos e pergunta "Bem, parece que você renomeou o nome do campo para first_name no modelo User. Deseja criar uma migração para ele?" No Django, ele funciona quase que magicamente, outras ferramentas que eu usei assumem a mesma abordagem de migração que eu mencionei acima: você é responsável por escrever as migrações, entendendo profundamente qual campo de qual tipo adicionar para ser real nos estados atuais do modelo
f1nn
2
Você pode se livrar pendinge depois executefazer umzug.up().then(function (migrations) { app.listen(3000); }). De acordo com a documentação do umzug, isso executará todas as migrações pendentes.
Vinay #
Quando você conclui a migração, é comum adicionar os campos ao esquema no arquivo de modelo original?
theptrk
@ f1nn Tenho uma pergunta sobre a sua configuração, como você lida com o cluster e a disponibilidade de aplicativos? Vou integrar o pm2 no meu fluxo de trabalho e talvez não funcione diretamente com os scripts npm.
27616 diosney
17

Estou aprendendo isso sozinho, mas acho que recomendo usar as migrações agora para que você se acostume. Eu descobri que o melhor para descobrir o que ocorre na migração é examinar o sql nas tabelas criadas por sequelize.sync()e depois criar as migrações a partir daí.

migrations -c [migration name] 

Criará o arquivo de migração de modelo em um diretório de migrações. Você pode preenchê-lo com os campos que você precisa criar. Este arquivo precisará incluir createdAt/updatedAt , campos necessários para associações, etc.

Para a criação da tabela inicial, o down deve ter:

migration.dropTable('MyTable');

Porém, atualizações subseqüentes na estrutura da tabela podem deixar isso de fora e usar apenas alterar tabela.

./node_modules/.bin/sequelize --migrate

Um exemplo de criação seria semelhante 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();
  }

Para refazer desde o início:

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

Estou usando o café para executar um arquivo de sementes para preencher as tabelas após:

coffee server/seed.coffee

Isso apenas tem uma função de criação que se parece com:

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

Lembre-se de levar seu sync() índice de seus modelos ou ele substituirá o que as migrações e as sementes fazem.

Os documentos estão em http://sequelize.readthedocs.org/en/latest/docs/migrations/, é claro. Mas a resposta básica é que você precisa adicionar tudo para especificar os campos necessários. Não faz isso por você.

user1916988
fonte
5
Eu não estava perguntando como criar e executar migrações - como você apontou, isso está disponível na documentação. O que eu estava perguntando é como usá-los no contexto de um aplicativo reutilizável, em que as instâncias existentes precisam ser atualizadas para uma versão mais recente do banco de dados e as novas instâncias precisam desse banco de dados feito do zero. Ou talvez você esteja respondendo e dizendo que não devo usar sync (), fazendo o banco de dados inicial e todas as alterações nele nas migrações. É isso que você está dizendo?
tremby
1
@ tremby Acho que é isso que ele está dizendo. Você pode usar a sincronização e lidar com os resultados ou criar as migrações manualmente. Nossas estruturas, à moda do Rails, geram arquivos de migração com base em uma diferença de esquema, eu adoraria que Sequelize fizesse isso por mim. Demasiada de uma dor de fazer migrações manualmente ...
mpowered
É uma pena que você não possa sequelize.sync()ter um script gerado que crie todas as tabelas e índices base como sua primeira migração (semelhante ao rails ' schema.rb). Depois de ler isso, parece que sua melhor aposta pode ser exportar seu esquema inicial como sql, coloque-o em uma grande execdeclaração na sua primeira migração. A partir daí, você estará executando alterações incrementais em um ponto de partida conhecido da "versão 1.0".
Thom_nic 5/09
11

Para o desenvolvimento , agora existe uma opção para sincronizar as tabelas atuais alterando sua estrutura. Usando a versão mais recente do repositório de sequências do github , agora você pode executar a sincronização com o alterparâmetro

Table.sync({alter: true})

Uma ressalva dos documentos:

Altera tabelas para caber nos modelos. Não recomendado para uso em produção. Exclui dados em colunas que foram removidas ou tiveram seu tipo alterado no modelo.

meyer9
fonte
3

Agora, com a nova sequela da migração, é muito simples.

Este é um exemplo do que você pode fazer.

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

Lembre-se de que você deve definir:

"dialectOptions": { "multipleStatements": true }

na configuração do banco de dados.

Nestor Magalhães
fonte
Isso simplesmente não descarta e recria o banco de dados?
21416 Twilly
Eu acho que o uso de um arquivo sql grande inicial não é a maneira recomendada de fazê-lo, pois amarrará o adaptador e o banco de dados, que, caso contrário, serão independentes de banco de dados, pois você pode usar para desenvolvimento sqlite e para produção mariadb ou outra.
Diosney 27/07/16
2

Use a versão. A versão do aplicativo depende da versão do banco de dados. Se a nova versão exigir uma atualização de um banco de dados, crie uma migração para ele.

update: decidi abandonar a migração ( KISS ) e executar o script update_db (sync forse: false) quando necessário.

Sergey Karasev
fonte
Semelhante à minha resposta à resposta de user1916988, você está dizendo que eu não deveria usá sync()-lo e que preciso escrever manualmente as migrações do esquema dos modelos da versão anterior para os modelos da versão mais recente?
tremby
Fiz +1 por causa da sua atualização. Na verdade, estou pensando em fazer o mesmo. Escrever todas as migrações manualmente quando o aplicativo pode fazer isso é um pouco estúpido, então vou criar um script manual que executa o aplicativo uma vez e executa a função de sincronização.
Sallar
2

Um pouco tarde, e depois de ler a documentação, você não precisa ter a primeira migração de que está falando. Tudo o que você precisa fazer é ligar syncpara criar as tabelas.

sequelize.sync()

Você também pode executar uma sincronização de modelo simples, fazendo algo como:

Project.sync()mas acho que esse sequelize.sync()é um caso geral mais útil para o seu projeto (desde que você importe os bons modelos na hora de início).

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

Isso criará todas as estruturas iniciais . Depois, você só precisará criar migrações para evoluir seus esquemas.

espero que ajude.

kiddouk
fonte
7
Eu não acho que você leu o post original muito bem, ou talvez eu não tenha sido suficientemente claro. Estou mais do que consciente sequelize.sync()e o que faz.
tremby
2

Sequelize pode executar SQL arbitrário de forma assíncrona .

O que eu faria é:

  • Gerar uma migração (para usar como primeira migração);
  • Despejar seu banco de dados, algo como: mysql_dump -uUSER -pPASS DBNAME > FILE.SQL
  • Cole o despejo completo como texto (Perigoso) ou carregue um arquivo com o despejo completo no Nó:
    • 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');
  • Execute este despejo em Sequelize Migration:
module.exports = {
  up: function (migration, DataTypes) {
    var baseSQL = "whatever" // I recommend loading a file
    migration.migrator.sequelize.query(baseSQL);
  }
}

Isso deve cuidar da configuração do banco de dados, embora a coisa assíncrona possa se tornar um problema. Se isso acontecer, eu procuraria uma maneira de adiar o retorno da upfunção sequelize até que a queryfunção assíncrona seja concluída.

Mais sobre o mysql_dump: http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html
Mais sobre Sequelizar migrações: http://sequelize.readthedocs.org/en/latest/docs/migrations/
Mais sobre Executando o SQL na Migração de Sequelize: https://github.com/sequelize/sequelize/issues/313

Fernando Cordeiro
fonte
1

Aqui está o meu fluxo de trabalho atual. Estou aberto a sugestões.

  1. Configure sequelize para criar tabelas que não existem
  2. Configure sequelize para descartar e recriar todas as tabelas em um banco de dados em branco chamado _blank
  3. Use uma ferramenta mysql para comparar _blank ee sincronizar alterações usando essa ferramenta. Ainda à procura de uma ferramenta acessível que possa fazer isso no mac. O ambiente de trabalho do MySql parece que você pode importar um modelo de um esquema existente e depois sincronizar o esquema. Tentando descobrir como fazer isso via linha de comando para facilitar.

Dessa forma, você não precisa atualizar manualmente a tabela de migrações e precisa se preocupar com dedos gordos, mas ainda recebe um ORM.

TWilly
fonte
1

Amigo, tive a mesma pergunta e consegui entender como usá-los.

Comecei sem ORM sequelizar, portanto, eu já tinha um modelo de dados.
Eu tive que gerar os modelos automaticamente com sequelize-auto e gerar suas migrações com este arquivo que você cria https://gist.github.com/ahelord/a7a7d293695b71aadf04157f0f7dee64 e coloca em sync ( {Force: false})
Isto está em dev.I teria de versão o modelo e as migrações e as executo sempre que puxo o código.

Na produção, o servidor está apenas no andar de cima; portanto, você só precisa executar migrações e, em cada confirmação, gerenciar, pois fará a versão do modelo sem parar o back-end.

Leonardo Rodriguez
fonte
1

Eu passei por este post e perguntas semelhantes, ele realmente não respondeu para mim. As migrações são úteis para girar bancos de dados locais e atualizar dados na produção

Fiz a pergunta aqui e também a respondi: Fluxo de trabalho para lidar com sequências de migrações e inicialização?

Versão TL-DR para um projeto greenfield

  1. Projete seu esquema de banco de dados como faria tradicionalmente usando scripts SQL puros ou se você usar uma ferramenta GUI
  2. Quando você finalizar todos os seus 95% do esquema db e ficar satisfeito com ele, vá em frente e mova-o para sequenciar movendo o .sqlarquivo inteiro sobre
  3. Faça sua primeira migração. Execute sequelize init:migratena pasta onde você modelsestiver
  4. Faça seu primeiro arquivo de migração. Corresequelize migration:generate --name [name_of_your_migration]
  5. Nesse arquivo de migração, coloque esse código lá
("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;
`),
};

insira a descrição da imagem aqui

com essa estrutura geral de pastas

insira a descrição da imagem aqui

  1. Agora, sua configuração sequencial é sincronizada com o esquema inicial do banco de dados
  2. quando você quiser editar o esquema do banco de dados, execute-o novamente sequelize migration:generate --name [name_of_your_migration]
  3. Vá em frente e faça suas modificações aqui nos caminhos upe de downmigração. Estas são as suas instruções ALTER para alterar nomes de colunas, DELETE, ADD etc
  4. Corre sequelize db:migrate
  5. Você deseja que os modelos sejam sincronizados com as alterações no seu banco de dados remoto, então o que você pode fazer agora é npm install sequelize-auto.
  6. Isso lerá o esquema atual do banco de dados no banco de dados e gerará automaticamente os arquivos de modelo. Use um comando semelhante ao sequelize-auto -o "./models" -d sequelize_auto_test -h localhost -u my_username -p 5432 -x my_password -e postgresencontrado em https://github.com/sequelize/sequelize-auto

Você pode usar o git para ver os difflogs em seu modelo; deve haver apenas mudanças refletindo alterações no modelo de banco de dados. Como uma observação lateral, nunca modifique o modelsdiretamente se você o usar sequelize auto, pois isso irá gerá-los para você. Da mesma forma, você não deve mais modificar seu esquema de banco de dados diretamente com arquivos SQL, desde que essa seja uma opção, pois você também pode importar esses .sqlarquivos

Agora, o esquema do seu banco de dados está atualizado e você mudou oficialmente para sequenciar apenas as migrações do banco de dados.

Tudo é controlado por versão. Esse é o fluxo de trabalho ideal para desenvolvedores de banco de dados e back-end

Vincent Tang
fonte
0

Existe uma maneira ainda mais simples (evitando Sequalize). Que é assim:

  1. Você digita um comando dentro do seu projeto: npm run migrate: new

  2. Isso cria 3 arquivos. Um arquivo js e dois arquivos sql nomeados para cima e para baixo

  3. Você coloca sua instrução SQL nesses arquivos, que é sql puro
  4. Em seguida, você digita: npm run migrate: up ou npm run migrate: down

Para que isso funcione, consulte o db-migrate módulo .

Depois de configurá-lo (o que não é difícil), alterar seu banco de dados é realmente fácil e economiza muito tempo.

Vedran Maricevic.
fonte