2017-10-16 95 views
-2

我想在knex建立外鍵下面的表格:種子外鍵約束

評論

+----+---------+-------------------+------------+------------+------------+-----------+ 
| id | post_id | comment   | is_deleted | createdAt | updatedAt | deletedAt | 
+----+---------+-------------------+------------+------------+------------+-----------+ 
| 1 | 2  | This is a comment | false  | 16.10.2017 | 16.10.2017 |   | 
+----+---------+-------------------+------------+------------+------------+-----------+ 

+----+-----------------+------------------+---------+------------+------------+-----------+ 
| id | titel   | description  | deleted | createdAt | updatedAt | deletedAt | 
+----+-----------------+------------------+---------+------------+------------+-----------+ 
| 1 | This is a titel | Test Description | false | 16.10.2017 | 16.10.2017 |   | 
+----+-----------------+------------------+---------+------------+------------+-----------+ 
| 2 | Titel Test  | Test Description | false | 16.10.2017 | 16.10.2017 |   | 
+----+-----------------+------------------+---------+------------+------------+-----------+ 

我創建了以下遷移:

comments.js

exports.up = function (knex, Promise) { 
    return knex.schema.createTable("comments", function (t) { 
    t.increments("id").unsigned().primary().references('id').inTable('posts') 
    t.text("comment").nullable() 
    t.boolean("is_deleted").nullable() 
    t.dateTime("createdAt").notNull() 
    t.dateTime("updatedAt").nullable() 
    t.dateTime("deletedAt").nullable() 
    }) 
} 

posts.js

exports.up = function (knex, Promise) { 
    return knex.schema.createTable('posts', function (t) { 
     t.increments('id').unsigned().primary(); 
     t.string('title').notNull(); 
     t.text('description').nullable(); 
     t.boolean('deleted').nullable();  
     t.dateTime('createdAt').notNull(); 
     t.dateTime('updatedAt').nullable(); 
     t.dateTime('deletedAt').nullable(); 
    }); 
}; 

最後我想播種用假數據表:

const faker = require("faker") 
const knex = require("../db/knexfile.js") 
const _ = require("lodash") 
const postNumber = 50 
const commentNumber = 150 


function getRandomPostId() { 
    const numberOfPosts = knex("posts").count("title") 
    return _.random(0, numberOfPosts) 
} 

exports.seed = function(knex, Promise) { 
    return Promise.all([ 
    knex("posts").del() 
    .then(function() { 
     const posts = [] 
     for (let index = 0; index < postNumber; index++) { 
     posts.push({ 
      titel: faker.lorem.sentence(), 
      description: faker.lorem.sentence(), 
      createdAt: faker.date.recent(), 
      updatedAt: faker.date.recent(), 
      deletedAt: faker.date.recent(), 
      deleted: faker.random.boolean(), 
      tags: faker.random.arrayElement(["tag1", "tag2", "tag3", "tag4", ]), 
     }) 
     } 
     return knex("posts").insert(posts) 
    }), 
    knex("comments").del() 
    .then(function() { 
     const comments = [] 
     for (let index = 0; index < commentNumber; index++) { 
     comments.push({ 
      comment: faker.lorem.sentence(), 
      createdAt: faker.date.recent(), 
      deletedAt: faker.date.recent(), 
      updatedAt: faker.date.recent(), 
      is_deleted: faker.date.recent(), 
     }) 
     } 
     return knex("comments").insert(comments) 
    }) 

    ]) 
} 

不過,我得到以下錯誤:

Using environment: development 
Error: ER_NO_REFERENCED_ROW_2: Cannot add or update a child row: a foreign key constraint fails (`c9`.`comments`, CONSTRAINT `comments_id_foreign` FOREIGN KEY (`id`) REFERENCES `posts` (`id`)) 
    at Query.Sequence._packetToError (/home/ubuntu/workspace/node_modules/mysql/lib/protocol/sequences/Sequence.js:52:14) 
    at Query.ErrorPacket (/home/ubuntu/workspace/node_modules/mysql/lib/protocol/sequences/Query.js:77:18) 
    at Protocol._parsePacket (/home/ubuntu/workspace/node_modules/mysql/lib/protocol/Protocol.js:279:23) 
    at Parser.write (/home/ubuntu/workspace/node_modules/mysql/lib/protocol/Parser.js:76:12) 
    at Protocol.write (/home/ubuntu/workspace/node_modules/mysql/lib/protocol/Protocol.js:39:16) 
    at Socket.<anonymous> (/home/ubuntu/workspace/node_modules/mysql/lib/Connection.js:103:28) 
    at emitOne (events.js:96:13) 
    at Socket.emit (events.js:188:7) 
    at readableAddChunk (_stream_readable.js:176:18) 
    at Socket.Readable.push (_stream_readable.js:134:10) 
    at TCP.onread (net.js:547:20) 

是我的外鍵約束錯誤?
任何建議爲什麼我得到這個錯誤?

我感謝您的回覆!

回答

0

在遷移創建方面,嘗試分離post_id外鍵生成。它看起來像它試圖使用comments.id既作爲主鍵和外鍵,而不是一個名爲post_id一個單獨的列引用posts.id

遷移:

exports.up = function (knex, Promise) { 
    return knex.schema.createTable('posts', function (t) { 
     t.increments().unsigned().primary(); 
     t.string('title').notNull(); 
     t.text('description').nullable(); 
     t.boolean('deleted').nullable();  
     t.dateTime('createdAt').notNull(); 
     t.dateTime('updatedAt').nullable(); 
     t.dateTime('deletedAt').nullable(); 
    }); 
}; 

exports.up = function (knex, Promise) { 
    return knex.schema.createTable("comments", function (t) { 
    t.increments().unsigned().primary(); 
    t.text("comment").nullable(); 
    t.boolean("is_deleted").nullable(); 
    t.dateTime("createdAt").notNull(); 
    t.dateTime("updatedAt").nullable(); 
    t.dateTime("deletedAt").nullable(); 
    // column with name post_id references posts.id 
    t.foreign("post_id").references('id').inTable('posts'); 
    // or 
    // t.foreign("post_id").references('posts.id'); 
    }) 
}; 

有關錯誤的,它看起來像這主要是時間問題。 Promise.all()不會按順序運行/執行/啓動承諾任務。這意味着當創建評論時,它可能沒有有效的posts.id來關聯。通過更新遷移,您最好等到創建所有帖子後,獲取現有帖子ID值,然後使用這些值創建具有有效post_id約束的註釋。 Knex方法pluck()在這裏派上用場,因爲您可以獲取posts.id值的數組。我也考慮過可能會把這個問題分解成多個種子,因爲我曾經遇到過問題,即將大於100的數據插入給定的表中。您可以通過在每個循環中插入來解決此問題,但需要更長的時間,但似乎沒有經歷相同的限制。如果每條評論都需要與帖子相關聯,但似乎沒有發生在當前種子上,那麼看起來並沒有插入任何post_id或類似內容。下面的代碼在每次迭代時抓取有效的隨機posts.id,並將其分配給comments.post_id,滿足約束條件。

從我所看到的將是如下播種順序:

  1. 刪除評論
  2. 刪帖
  3. 創建的帖子
  4. 使用有效的意見創建/現有職位的id值

種子:

exports.seed = function(knex, Promise) { 
    return knex("comments").del() 
     .then(() => { 
      return knex("posts").del(); 
     }) 
     .then(() => { 
      const posts = []; 

      for (let index = 0; index < postNumber; index++) { 
       posts.push({ 
        title: faker.lorem.sentence(), 
        description: faker.lorem.sentence(), 
        createdAt: faker.date.recent(), 
        updatedAt: faker.date.recent(), 
        deletedAt: faker.date.recent(), 
        deleted: faker.random.boolean(), 
        tags: faker.random.arrayElement(["tag1", "tag2", "tag3", "tag4", ]), 
       }); 
      } 

      return knex("posts").insert(posts); 
     }) 
     .then(() => { 
      return knex('posts').pluck('id').then((postIds) => { 
       const comments = []; 

        for (let index = 0; index < commentNumber; index++) { 
         comments.push({ 
          comment: faker.lorem.sentence(), 
          createdAt: faker.date.recent(), 
          deletedAt: faker.date.recent(), 
          updatedAt: faker.date.recent(), 
          is_deleted: faker.date.recent(), 
          post_id: faker.random.arrayElement(postIds) 
         }) 
        } 

       return knex("comments").insert(comments); 
      });     
     }); 
}; 

注:種子內,title在創造職位是誤拼爲titel。不知道你是否想要使用titel而不是title,但它需要保持一致。

希望這有助於!

0

看起來您需要更正您的評論表映射,如下所示,以在post_id列上添加約束條件。您的映射看起來似乎創建了外鍵(註釋的表格ID指的是帖子的ID)。

exports.up = function (knex, Promise) { 
    return knex.schema.createTable("comments", function (t) { 
    t.increments("id").unsigned().primary() 
    t.integer("post_id").references('id').inTable('posts') 
    t.text("comment").nullable() 
    t.boolean("is_deleted").nullable() 
    t.dateTime("createdAt").notNull() 
    t.dateTime("updatedAt").nullable() 
    t.dateTime("deletedAt").nullable() 
    }) 
} 

其次,您需要考慮遷移/種子文件是按其名稱的自然順序挑選的。在你的情況下,comments.js文件比posts.js文件更早執行,導致插入失敗,原因很明顯。

爲了解決這個問題,你可以重命名post.js20170812_10_posts.jscomments.js20170812_20_comments.js(對於e..g)。前綴<date>_<seq>_僅僅是一個建議的慣例。只要命名遷移/種子文件的名稱符合自然順序,您可以遵循任何慣例。

相關的問題: https://github.com/tgriesser/knex/issues/993

希望這有助於。