2017-10-16 103 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

希望这有助于。