【问题标题】:How to do knex.js migrations?如何进行 knex.js 迁移?
【发布时间】:2014-05-02 17:21:31
【问题描述】:

我仍然不确定如何使用 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;')

  ]);
};

【问题讨论】:

    标签: javascript knex.js


    【解决方案1】:

    主观上,作为最干净的方法,我建议在您的迁移文件中包含以下内容:

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

    【讨论】:

      【解决方案2】:

      我想我会更新这个,因为对 Javascript 进行了一些补充,使这变得相当容易。 knex 仍然要求您返回 Promise 但在 Promise 内部,您可以做很多事情来清理与表的创建/修改相关的代码。我的偏好是使用async/awaitPromise.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);
              }
          })
      }
      

      这让事情变得更干净,不需要您将一堆thens 菊花链在一起或将它们包装在周围的函数中。您只需await 为每个表创建解析,然后解析它们的封装Promise!赞async/await

      您也可以按照此模式在 down 迁移中删除表。只需将 knex.schema.createTable 语句替换为 knex.schema.dropTable 语句即可。

      【讨论】:

        【解决方案3】:

        jedd.ahyoung 是正确的。您无需将连接池限制为 1。您只需要链接您的 Promise,以便它们不会并行运行。

        例如:

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

        【讨论】:

          【解决方案4】:

          inTable() 应该放在references() 之后:

          inTablecolumn.inTable(table)

          设置调用column.references后外键列所在的table

          Documentation.

          【讨论】:

            【解决方案5】:

            我通过事务解决了这个问题

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

            【讨论】:

            • 谁能详细说明transaction是什么以及为什么需要SET foreign_key_checks
            【解决方案6】:

            发现由于连接池,它无法正常工作。它将使用不同的连接来运行每个迁移任务,这会导致未正确设置外键检查。设置

            pool:{
              max:1
            }
            

            在迁移配置文件中修复了这个问题。

            【讨论】:

            • 看来真正的问题是函数可以并行运行。与下面的事务答案类似,您可能需要以特定方式链接您的 Promise 以获得您想要的。
            猜你喜欢
            • 2021-10-17
            • 2017-10-18
            • 2021-07-12
            • 1970-01-01
            • 2018-02-19
            • 2023-02-22
            • 2021-01-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多