How to do knex.js migrations?

旧时模样 提交于 2019-12-09 04:18:19

问题


I'm still not sure how to do my migrations with knex. Here is what I have so far. It works on up, but down gives me FK constraint error even though 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 is correct. You don't need to limit your connection pool to 1. You just need to chain your promises so they are not run in parallel.

For example:

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

Also I may be missing something but it looks like you won't need to remove and then reinstate the foreign key checks if you create the address table before the member table.

Here's how the final code would look:

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:


Figured out that it wasn't working because of connection pooling. It would use a different connection to run each migration task which caused foreign key checks not to be set properly. setting

pool:{
  max:1
}

in the migration config file fixed this.




回答3:


I solved this problem by using a transaction

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

Migration file

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() should be placed after references():

inTablecolumn.inTable(table)

Sets the table where the foreign key column is located after calling column.references.

Documentation.




回答5:


I thought I'd update this, since there have been some additions to Javascript that make this quite a bit easier. knex still requires that you return a Promise but inside that Promise you can do a lot of things to clean up the code related to the creation/modification of tables. My preference is to use a combination of async/await and 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);
        }
    })
}

If you prefer to create each table individually, then I'd just use 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);
        }
    })
}

This keeps things a lot cleaner, not requiring you to daisy chain a bunch of thens or wrap them in surrounding functions. You just await for each table creation to resolve and then resolve their encapsulating Promise! Yay for async/await!

You can follow this pattern for the dropping of tables in your down migration, too. Simply replace the knex.schema.createTable statements with knex.schema.dropTable statements.




回答6:


Subjectively as the most clean way to do it I would suggest including in your migration file something like:

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


来源:https://stackoverflow.com/questions/22624879/how-to-do-knex-js-migrations

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!