Как выполнить переходы 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')
])
}