How to do knex.js migrations?

前端 未结 6 1444
天命终不由人
天命终不由人 2021-02-01 19:39

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 fo

相关标签:
6条回答
  • 2021-02-01 19:43

    inTable() should be placed after references():

    inTablecolumn.inTable(table)

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

    Documentation.

    0 讨论(0)
  • 2021-02-01 19:55

    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.

    0 讨论(0)
  • 2021-02-01 19:57

    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.

    0 讨论(0)
  • 2021-02-01 20:02

    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();
        });
      }
    };
    
    0 讨论(0)
  • 2021-02-01 20:05

    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);
    
    0 讨论(0)
  • 2021-02-01 20:05

    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')
      ])
    }
    
    0 讨论(0)
提交回复
热议问题