Sequelize.js: как использовать миграции и синхронизацию

137

Я близок к тому, чтобы мой проект был готов к запуску. У меня большие планы после запуска, и структура базы данных изменится - новые столбцы в существующих таблицах, а также новые таблицы и новые ассоциации с существующими и новыми моделями.

Я еще не коснулся миграций в Sequelize, так как у меня было только тестирование данных, которые я не против стирать каждый раз при изменении базы данных.

С этой целью в настоящее время я работаю, sync force: trueкогда мое приложение запускается, если я изменил определения модели. Это удаляет все таблицы и делает их с нуля. Я мог бы опустить forceопцию, чтобы он только создавал новые таблицы. Но если существующие изменились, это бесполезно.

Так как я добавляю в миграции, как все работает? Очевидно, я не хочу, чтобы существующие таблицы (с данными в них) были стерты, поэтому об этом не sync force: trueможет быть и речи. В других приложениях, которые я помогал разработать (Laravel и другие фреймворки) в рамках процедуры развертывания приложения, мы запускаем команду migrate для запуска любых отложенных миграций. Но в этих приложениях самая первая миграция имеет скелетную базу данных, причем база данных находится в состоянии, в котором она находилась на ранней стадии разработки - первый альфа-релиз или что-то еще. Таким образом, даже экземпляр приложения, опоздавшего на вечеринку, может набрать скорость за один раз, выполнив все миграции последовательно.

Как мне создать такую ​​«первую миграцию» в Sequelize? Если у меня его нет, у нового экземпляра приложения, находящегося где-то вниз по линии, либо не будет скелетной базы данных для запуска миграций, либо он запустит синхронизацию в начале и переведет базу данных в новое состояние со всеми новые таблицы и т. д., но затем, когда он попытается выполнить миграцию, они не будут иметь смысла, так как они были написаны с учетом исходной базы данных и каждой последующей итерации.

Мой мыслительный процесс: на каждом этапе исходная база данных плюс каждая последовательная миграция должны равняться (плюс или минус данные) базе данных, генерируемой при sync force: trueэто запустить. Это потому, что описания моделей в коде описывают структуру базы данных. Поэтому, возможно, если нет таблицы миграции, мы просто запускаем синхронизацию и помечаем все миграции как выполненные, даже если они не выполнялись. Это то, что мне нужно сделать (как?), Или Sequelize должен делать это сам, или я лаю не на том дереве? И если я нахожусь в правильной области, конечно, должен быть хороший способ автоматически генерировать большую часть миграции, учитывая старые модели (с помощью хэша коммитов? Или даже каждую миграцию можно привязать к коммиту? в непортативной вселенской git) и новые модели. Он может различать структуру и генерировать команды, необходимые для преобразования базы данных из старой в новую и обратно, а затем разработчик может пойти и внести любые необходимые изменения (удаление / перенос определенных данных и т. Д.).

Когда я запускаю двоичный файл sequelize с помощью --initкоманды, он дает мне пустой каталог миграций. Когда я запускаю sequelize --migrateего, я получаю таблицу SequelizeMeta, в которой ничего нет, никаких других таблиц. Очевидно, нет, потому что этот двоичный файл не знает, как загрузить мое приложение и загрузить модели.

Я должен что-то упустить.

TLDR: как настроить мое приложение и его миграции, чтобы можно было обновлять различные экземпляры живого приложения, а также новое приложение без устаревшей стартовой базы данных?

tremby
источник
2
Я ответил относительно вашего рабочего процесса, но в идеале все таблицы должны быть настроены с помощью миграций. Даже если вы используете syncсейчас, идея состоит в том, что миграции «генерируют» всю базу данных, поэтому полагаться на скелет само по себе является проблемой. Например, рабочий процесс Ruby on Rails использует Migrations для всего, и это довольно здорово, когда вы к этому привыкнете. Редактировать: И да, я заметил, что этот вопрос довольно старый, но, поскольку никогда не было удовлетворительного ответа, и люди могут приходить сюда в поисках руководства, я решил, что должен внести свой вклад.
Фернандо Кордейру

Ответы:

88

Генерация «первой миграции»

В вашем случае самый надежный способ - сделать это практически вручную. Я бы предложил использовать инструмент sequelize-cli . Синтаксис довольно прост:

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

Это создаст модель и миграцию. Затем вручную объедините существующие модели с созданными с помощью sequelize-cli и сделайте то же самое с миграциями. После этого сотрите базу данных (если возможно) и запустите

sequelize db:migrate

Это создаст схему миграции. Вы должны сделать это только один раз, чтобы переключиться на правильный процесс разработки схемы (без sync: force, но с принудительными миграциями).

Позже, когда вам нужно изменить схему:

  1. Создать миграцию: sequelize migration:create
  2. Запишите вверх и вниз функции в вашем файле миграции
  3. В соответствии с изменениями в файле миграции измените модель вручную.
  4. Бегать sequelize db:migrate

Запуск миграций на производстве

Очевидно, что вы не можете подключиться к производственному серверу и выполнить миграцию вручную. Используйте umzug , независимый от среды инструмент миграции для Node.JS, чтобы выполнить отложенные миграции до запуска приложения.

Вы можете получить список ожидающих / еще не выполненных миграций:

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

Затем выполните миграцию ( внутри обратного вызова ). Метод execute - это функция общего назначения, которая запускает для каждой указанной миграции соответствующую функцию:

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

И я предлагаю сделать это до запуска приложения и каждый раз пытаться обслуживать маршруты. Что-то вроде этого:

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

Я не могу попробовать это прямо сейчас, но на первый взгляд это должно работать.

UPD апрель 2016

Через год все еще полезно, поэтому делюсь своими текущими советами. Сейчас я устанавливаю sequelize-cliпакет, как требуется, в режиме реального времени , а затем изменяю сценарии запуска NPM package.jsonследующим образом:

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

Единственное, что мне нужно сделать на рабочем сервере, это npm start. Эта команда запустит все миграции, применит все сидеры и запустит сервер приложений. Не нужно звонить в умзуг вручную.

f1nn
источник
3
Это звучит как то, что я ищу. Это не так волшебно и автоматически, как "должно" быть, но, возможно, это лучшее, на что можно надеяться. Однако в настоящее время я не работаю с Sequelize и не смогу проверить это в ближайшее время. Но если кто-то еще согласится, что это решение хорошо, я приму этот ответ. Я все еще нахожу немного грустным, что, кажется, нет никакого способа автоматически выполнить эти миграции из различий между версиями модели.
Тремби
4
@tremby единственная среда, которую я использовал, которая действительно понимает модели, была Django. Он анализирует модели и спрашивает: «Что ж, кажется, вы переименовали имя поля в first_name в модели User. Хотите создать для него миграцию?» В Django это работает почти волшебно, другие инструменты, которые я использовал, предполагают тот же подход к миграции, о котором я упоминал выше: вы сами ответственны за написание миграций, глубоко понимая, какое поле какого типа добавить, чтобы оно было актуальным для ваших текущих состояний модели
f1nn
2
Вы можете избавиться, pendingа затем executeи просто сделать umzug.up().then(function (migrations) { app.listen(3000); }). Согласно документации umzug, он выполнит все ожидающие миграции.
Vinay
Когда вы завершаете миграцию, обычно ли добавляются поля к схеме в исходном файле модели?
Theptrk
@ f1nn У меня есть вопрос о вашей настройке, как вы справляетесь с кластеризацией и доступностью приложений? Я буду интегрировать pm2 в свой рабочий процесс, и, возможно, он не будет работать напрямую со сценариями npm.
diosney
17

Просто изучаю это сам, но я думаю, что сейчас рекомендую использовать миграции, чтобы вы к ним привыкли. Я обнаружил, что для выяснения того, что происходит в процессе миграции, лучше всего взглянуть на sql созданных таблиц, sequelize.sync()а затем построить миграции оттуда.

migrations -c [migration name] 

Создаст файл миграции шаблона в каталоге миграции. Затем вы можете заполнить его полями, которые вам нужны. Этот файл должен включать createdAt/ updatedAt, поля, необходимые для ассоциаций и т. Д.

Для первоначального создания таблицы вниз должен иметь:

migration.dropTable('MyTable');

Но последующие обновления структуры таблицы могут не учитывать это и просто использовать alter table.

./node_modules/.bin/sequelize --migrate

Пример создания будет выглядеть так:

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

Чтобы повторить с начала:

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

Я использую кофе, чтобы запустить начальный файл для заполнения таблиц после:

coffee server/seed.coffee

Это просто имеет функцию создания, которая выглядит примерно так:

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

Не забудьте убрать свой sync()индекс из ваших моделей, иначе он перезапишет то, что делают миграции и начальное число.

Документы можно найти по адресу http://sequelize.readthedocs.org/en/latest/docs/migrations/ . Но основной ответ заключается в том, что вы должны добавить в себя все, чтобы указать нужные поля. Это не делает это для вас.

user1916988
источник
5
Я не спрашивал, как создавать и запускать миграции - как вы указали, все это доступно в документации. Я спрашивал, как их использовать в контексте повторно используемого приложения, где существующие экземпляры должны быть обновлены до более новой версии базы данных, а новым экземплярам нужна эта база данных, созданная с нуля. Или , возможно , вы будете отвечать , что и говорить , что я не должен использовать синхронизацию () на всех, и делаете начальную базу данных и все изменения к нему в миграции. Это то, что вы говорите?
Tremby
1
@tremby Я думаю, это то, что он говорит. Вы можете использовать синхронизацию и работать с результатами, или вы можете создавать миграции все вручную. Наши фреймворки, в стиле Rails, генерируют файлы миграции на основе различий схемы, я бы ЛЮБИЛ, если бы Sequelize сделал это для меня. Слишком много боли, чтобы делать миграции вручную ...
mpowered
Жаль, что тогда у вас не может sequelize.sync()быть сгенерированного скрипта, который создает все базовые таблицы и индексы в качестве первой миграции (аналогично rails ' schema.rb.) После прочтения этого, вам, вероятно, лучше всего будет экспортировать исходную схему как sql, а затем внесите это в большой execоператор в вашей первой миграции. Затем вы запускаете инкрементные изменения для известной отправной точки "версии 1.0".
thom_nic
11

Для разработки теперь есть возможность синхронизировать текущие таблицы, изменяя их структуру. Используя последнюю версию репозитория sequelize github , теперь вы можете запустить синхронизацию с alterпараметром.

Table.sync({alter: true})

Предостережение от документов:

Изменяет таблицы, чтобы соответствовать моделям. Не рекомендуется для производственного использования. Удаляет данные в столбцах, которые были удалены или изменили их тип в модели.

meyer9
источник
3

Теперь с новой последовательностью миграции очень просто.

Это пример того, что вы можете сделать.

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

Помните, что вы должны установить:

"dialectOptions": { "multipleStatements": true }

в конфигурации базы данных.

Нестор Магальес
источник
Разве это не просто удалить и заново создать базу данных?
TWilly
Я думаю, что использование исходного большого файла sql не рекомендуется для этого, так как он связывает адаптер и базу данных, которые в противном случае будут независимыми от базы данных, так как вы можете использовать для разработки sqlite и для производственного mariadb или другого.
diosney
2

Используйте версию. Версия приложения зависит от версии базы данных. Если новая версия требует обновления базы данных, создайте для нее миграцию.

обновление: я решил отказаться от миграции ( KISS ) и запустить скрипт update_db (sync forse: false), когда это необходимо.

Сергей Карасев
источник
Аналогично моему ответу на ответ user1916988, вы говорите, что я вообще не должен его использовать sync(), и что мне нужно вручную записать переносы со схем моделей более старых версий в модели более новых версий?
Тремби
+1 +1 из-за твоего обновления. Я действительно думаю сделать то же самое. Писать все миграции вручную, когда приложение может это сделать, немного глупо, так что я просто сделаю ручной сценарий, который запускает приложение один раз и запускает функцию синхронизации.
Саллар
2

Немного поздно, и после прочтения документации вам не понадобится та первая миграция, о которой вы говорите. Все, что вам нужно сделать, это позвонить sync, чтобы создать таблицы.

sequelize.sync()

Вы также можете запустить простую синхронизацию модели, выполнив что-то вроде:

Project.sync()но я думаю, что sequelize.sync()это более полезный общий случай для вашего проекта (если вы импортируете хорошие модели во время запуска).

(взято с http://sequelizejs.com/docs/latest/models#database-synchronization )

Это создаст все начальные структуры. После этого вам нужно будет только создать миграции, чтобы развить ваши схемы.

Надеюсь, поможет.

kiddouk
источник
7
Я не думаю, что вы прочитали оригинальное сообщение очень тщательно, или, возможно, я не был достаточно ясен. Я более чем осведомлен о sequelize.sync()том, что он делает.
Tremby
2

Sequelize может запускать произвольный SQL асинхронно .

Что бы я сделал, это:

  • Генерация миграции (для использования в качестве первой миграции);
  • Дамп вашей базы данных, что-то вроде: mysql_dump -uUSER -pPASS DBNAME > FILE.SQL
  • Либо вставьте полный дамп как текст (опасный), либо загрузите файл с полным дампом в Node:
    • 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');
  • Запустите этот дамп в Sequelize Migration:
module.exports = {
  up: function (migration, DataTypes) {
    var baseSQL = "whatever" // I recommend loading a file
    migration.migrator.sequelize.query(baseSQL);
  }
}

Это должно позаботиться о настройке базы данных, хотя асинхронность может стать проблемой. Если это произойдет, я бы посмотрел на способ отложить возврат upфункции sequelize до завершения асинхронной queryфункции.

Подробнее о mysql_dump: http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html
Подробнее о сиквелизированной миграции: http://sequelize.readthedocs.org/en/latest/docs/migrations/
Подробнее Запуск SQL из Sequelize Migration: https://github.com/sequelize/sequelize/issues/313

Фернандо Кордейру
источник
1

Вот мой текущий рабочий процесс. Я открыт для предложений.

  1. Установите sequelize для создания таблиц, которые не существуют
  2. Установите sequelize для удаления и повторного создания всех таблиц в пустой базе данных с именем _blank.
  3. Используйте инструмент mysql, чтобы сравнить _blank и синхронизировать изменения, используя этот инструмент. Все еще ищу доступный инструмент, который может сделать это на Mac. MySql Workbench выглядит так, как будто вы можете импортировать модель из существующей схемы, а затем синхронизировать схему. Попытка выяснить, как сделать это через командную строку, чтобы сделать это легко.

Таким образом, вам не нужно вручную обновлять таблицу миграции и беспокоиться о толстых пальцах, но вы все равно получаете ORM.

Твилли
источник
1

Друг, у меня был такой же вопрос и мне удалось понять, как их использовать.

Я начал без ORM sequelize, поэтому у меня уже была модель данных.
Мне пришлось генерировать модели автоматически с помощью sequelize-auto и генерировать их миграции с помощью этого файла, который вы создаете https://gist.github.com/ahelord/a7a7d293695b71aadf04157f0f7dee64 и помещаете в sync ( {Force: false}).
Это в dev.I, мне нужно для версии модель и миграции и выполнять их каждый раз, когда я вытащить код.

В рабочем состоянии сервер находится только наверху, поэтому вам нужно только запустить миграцию и в каждом коммите управлять, так как вы будете управлять версией модели, не останавливая бэкэнд

Леонардо Родригес
источник
1

Я просмотрел этот пост и другие подобные вопросы, но он мне не ответил. Миграции полезны для раскрутки локальных баз данных и обновления данных в рабочей среде.

Я задал вопрос здесь и также ответил на него: рабочий процесс для обработки последовательных миграций и инициализации?

Версия TL-DR для нового проекта

  1. Создайте схему базы данных так, как вы это обычно делаете, используя сценарии чистого SQL или если вместо этого вы используете графический интерфейс
  2. Когда вы доработаете все свои 95% своей схемы БД и будете довольны ею, продолжайте и перемещайте ее, чтобы завершить, переместив весь .sqlфайл поверх
  3. Сделайте свою первую миграцию. Запустите sequelize init:migrateв любой папке, где вы modelsнаходитесь в
  4. Создайте свой первый файл миграции. Бегатьsequelize migration:generate --name [name_of_your_migration]
  5. В этом файле переноса поместите этот код
("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;
`),
};

введите описание изображения здесь

с этой общей структурой папок

введите описание изображения здесь

  1. Теперь ваши настройки sequelize синхронизируются с вашей исходной схемой базы данных
  2. когда вы хотите редактировать схему базы данных, запустите это снова sequelize migration:generate --name [name_of_your_migration]
  3. Продолжайте вносить изменения здесь upи на downпути миграции. Это ваши операторы ALTER для изменения имен столбцов, DELETE, ADD столбцов и т. Д.
  4. Бегать sequelize db:migrate
  5. Вы хотите, чтобы модели были синхронизированы с изменениями в вашей удаленной базе данных, так что вы можете сделать сейчас npm install sequelize-auto.
  6. Это будет считывать текущую схему базы данных в вашей базе данных и автоматически генерировать файлы модели. Используйте команду, подобную этой, sequelize-auto -o "./models" -d sequelize_auto_test -h localhost -u my_username -p 5432 -x my_password -e postgresнайденную в https://github.com/sequelize/sequelize-auto

Вы можете использовать git, чтобы увидеть difflogs в вашей модели, должны быть только изменения, отражающие изменения в модели базы данных. Как примечание, никогда не изменяйте modelsнапрямую, если вы используете sequelize auto, так как это сгенерирует их для вас. Кроме того, вам больше не нужно изменять схему базы данных напрямую с помощью файлов SQL, если это опция, поскольку вы можете импортировать и эти .sqlфайлы.

Теперь ваша схема базы данных обновлена, и вы официально перешли к сиквелизации только миграций базы данных.

Все контролируется версией. Это идеальный рабочий процесс для базы данных и внутреннего разработчика

Винсент Тан
источник
0

Есть еще более простой способ (избегая Sequalize). Который идет так:

  1. Вы вводите команду внутри вашего проекта: npm run migrate: new

  2. Это создает 3 файла. Файл js и два файла sql с именами вверх и вниз

  3. Вы помещаете свой SQL-оператор в эти файлы, который является чистым SQL
  4. Затем введите: npm run migrate: вверх или npm run migrate: вниз

Чтобы это работало, взгляните на модуль db-migrate .

После того, как вы настроите его (что не сложно), изменение вашей БД действительно легко и экономит много времени.

Ведран Марицевич.
источник