Как выполнить переходы knex.js?

Я все еще не уверен, как выполнять мои миграции с помощью knex. Вот что я до сих пор. Он работает на up, но down дает мне ошибку ограничения FK, хотя foreign_key_checks = 0.

exports.up = function(knex, Promise) {
  return Promise.all([
    knex.raw('SET foreign_key_checks = 0;'),

    /* CREATE Member table */
    knex.schema.createTable('Member', function (table) {
      table.bigIncrements('id').primary().unsigned();
      table.string('email',50);
      table.string('password');

      /* CREATE FKS */
      table.bigInteger('ReferralId').unsigned().index();
      table.bigInteger('AddressId').unsigned().index().inTable('Address').references('id');
    }),

    /* CREATE Address table */
    knex.schema.createTable('Address', function (table) {
      table.bigIncrements('id').primary().unsigned();
      table.index(['city','state','zip']);

      table.string('city',50).notNullable();
      table.string('state',2).notNullable();
      table.integer('zip',5).unsigned().notNullable();
    }),

    knex.raw('SET foreign_key_checks = 1;')
  ]);
};

exports.down = function(knex, Promise) {
  return Promise.all([
    knex.raw('SET foreign_key_checks = 0;'),

    knex.schema.dropTable('Address'),

    knex.schema.dropTable('Member'),

    knex.raw('SET foreign_key_checks = 1;')

  ]);
};

Ответы

Ответ 1

jedd.ahyoung - это правильно. Вам не нужно ограничивать пул соединений до 1. Вам просто нужно связать ваш promises, чтобы они не запускались параллельно.

Например:

exports.up = function(knex, Promise) {
  return removeForeignKeyChecks()
    .then(createMemberTable)
    .then(createAddressTable)
    .then(addForeignKeyChecks);

  function removeForeignKeyChecks() {
    return knex.raw('SET foreign_key_checks = 0;');
  }

  function addForeignKeyChecks() {
    return knex.raw('SET foreign_key_checks = 1;');
  }

  function createMemberTable() {
    return knex.schema.createTable('Member', function (table) {
      table.bigIncrements('id').primary().unsigned();
      table.string('email',50);
      table.string('password');

      /* CREATE FKS */
      table.bigInteger('ReferralId').unsigned().index();
      table.bigInteger('AddressId').unsigned().index().inTable('Address').references('id');
    });
  }

  function createAddressTable() {
    return knex.schema.createTable('Address', function (table) {
      table.bigIncrements('id').primary().unsigned();
      table.index(['city','state','zip']);

      table.string('city',50).notNullable();
      table.string('state',2).notNullable();
      table.integer('zip',5).unsigned().notNullable();
    });
  }
};

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

Вот как выглядел бы последний код:

exports.up = function(knex, Promise) {
  return createAddressTable()
    .then(createMemberTable);

  function createMemberTable() {
    return knex.schema.createTable('Member', function (table) {
      table.bigIncrements('id').primary().unsigned();
      table.string('email',50);
      table.string('password');

      /* CREATE FKS */
      table.bigInteger('ReferralId').unsigned().index();
      table.bigInteger('AddressId').unsigned().index().inTable('Address').references('id');
    });
  }

  function createAddressTable() {
    return knex.schema.createTable('Address', function (table) {
      table.bigIncrements('id').primary().unsigned();
      table.index(['city','state','zip']);

      table.string('city',50).notNullable();
      table.string('state',2).notNullable();
      table.integer('zip',5).unsigned().notNullable();
    });
  }
};

Ответ 2

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

pool:{
  max:1
}

в файле конфигурации миграции исправлено это.

Ответ 3

Я решил эту проблему, используя транзакцию

transation.js

module.exports = function transaction(fn) {
    return function _transaction(knex, Promise) {
        return knex.transaction(function(trx) {
            return trx
                .raw('SET foreign_key_checks = 0;')
                .then(function() {
                    return fn(trx, Promise);
                })
                .finally(function() {
                    return trx.raw('SET foreign_key_checks = 1;');
                });
        });
    };
}

Файл миграции

var transaction = require('../transaction');

function up(trx, Promise) {
    return trx.schema
       .createTable('contract', function(table) {
           table.boolean('active').notNullable();                                              
           table.integer('defaultPriority').unsigned().references('priority.id');                             
           table.integer('defaultIssueStatus').unsigned().references('issueStatus.id');
           table.integer('owner').notNullable().unsigned().references('user.id'); 
       })

       .createTable('user', function (table) {
           table.increments('id').primary();
           table.datetime('createdAt');
           table.datetime('updatedAt');

           table.string('phoneNumber').notNullable().unique();
           table.string('password').notNullable();            
           table.string('name').notNullable().unique();       
           table.string('email');                             
           table.string('status');                            
           table.string('roles').defaultTo('user');           
           table.integer('contract').unsigned().references('contract.id');
       });
}

function down(trx, Promise) {
    return trx.schema
        .dropTable('contract')
        .dropTable('user');
}

exports.up = transaction(up);
exports.down = transaction(down);

Ответ 4

inTable() следует разместить после references():

inTablecolumn.inTable(table)

Устанавливает table, где столбец внешнего ключа находится после вызова column.references.

Документация.

Ответ 5

Я думал, что обновлю это, так как в Javascript есть некоторые дополнения, которые делают это немного проще. knex по-прежнему требует, чтобы вы вернули Promise, но внутри этого Promise вы можете многое сделать для очистки кода, связанного с созданием/модификацией таблиц. Я предпочитаю использовать комбинацию async/await и Promise.all.

exports.up = function(knex, Promise) {
    return new Promise(async (resolve, reject) => {
        try {
            await Promise.all([
                knex.schema.createTable('videos', table => {
                    table.increments('id');
                    table.string('title');
                    table.string('director');
                    table.json('meta');
                }),
                knex.schema.createTable('books', table => {
                    table.increments('id');
                    table.string('title');
                    table.string('author');
                    table.json('meta');
                })
            ]);

            console.log('Tables created successfully');
            resolve();
        } catch(error) {
            reject(error);
        }
    })
}

Если вы предпочитаете создавать каждую таблицу отдельно, я бы просто использовал async/await.

exports.up = function(knex, Promise) {
    return new Promise(async (resolve, reject) => {
        try {
            await knex.schema.createTable('videos', table => {
                table.increments('id');
                table.string('title');
                table.string('director');
                table.json('meta');
            });
            console.log('videos table created successfully!');
            await knex.schema.createTable('books', table => {
                table.increments('id');
                table.string('title');
                table.string('author');
                table.json('meta');
            });
            console.log('books table created successfully!');
            resolve();
        } catch(error){
            reject(error);
        }
    })
}

Это делает вещи намного чище, не требуя от вас последовательной цепочки связок then или оборачивания их в окружающие функции. Вы просто await разрешаете создание каждой таблицы, а затем разрешаете их инкапсуляцию Promise! Yay для async/await!

Вы также можете следовать этому шаблону для удаления таблиц в вашей миграции down. Просто замените операторы knex.schema.createTable на операторы knex.schema.dropTable.

Ответ 6

Субъективно, как наиболее чистый способ сделать это, я бы предложил включить в файл миграции что-то вроде:

exports.up = function (knex) {
  return Promise.all([
    knex.schema.createTable('users', function (table) {
      table.increments('id')
      table.string('username').notNullable()
      table.string('password').notNullable()
      table.string('service').notNullable()
      table.text('cookies')
      table.enu('status', ['active', 'disabled', 'need_login', 'failed']).defaultTo('need_login').notNullable()
      table.datetime('last_checked')
      table.timestamps()
    }),
    knex.schema.createTable('products', function (table) {
      table.increments()
      table.integer('user_id').unsigned().notNullable()
      table.string('title')
      table.decimal('price', 8, 2)
      table.text('notes')
      table.enu('status', ['to_publish', 'published', 'hidden', 'not_found']).notNullable()
      table.timestamps()
      table.foreign('user_id').references('id').inTable('users')
    }),
    knex.schema.createTable('messages', function (table) {
      table.increments()
      table.integer('user_id').unsigned().notNullable()
      table.integer('product_id').unsigned().notNullable()
      table.boolean('incoming')
      table.boolean('unread')
      table.text('text')
      table.timestamps()
      table.foreign('user_id').references('id').inTable('users')
      table.foreign('product_id').references('id').inTable('products')
    })
  ])
}

exports.down = function (knex) {
  return Promise.all([
    knex.schema.dropTable('messages'),
    knex.schema.dropTable('products'),
    knex.schema.dropTable('users')
  ])
}