2017-10-16 37 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の外部キー生成を分離してみてください。主キーと外部キーではなく、posts.id

移行参照post_idという名前の別の列の両方としてcomments.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という制限付きのコメントを作成するためにそれらの値を使用します。 Knoxメソッドpluck()は、posts.id値の配列を戻すことができるので、ここで便利です。私は多分、複数のシードに分割することを検討したいと思います。あなたは各ループに挿入することでこれを回避することができますが、それは時間がかかりますが、同じ制限を受けるようには見えません。すべてのコメントを投稿と関連付ける必要がある場合、現在のシードでは起こっていないようですが、post_idなどが挿入されているようには見えません。以下のコードは、各反復で有効なランダムposts.idを取得し、それをcomments.post_idに割り当て、制約を満たしています。

次のように私が見ることができるものから播種のための順序は次のようになります。

  1. 削除ポスト
  2. が投稿
  3. を作成し、コメントを削除し、有効/既存のポストidは
  4. 値使用して、コメントを作成します。

種子:

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

注:ポストの作成中に種子、titletitelとしてスペルミスました。 titeltitleよりも使用することを意味するのかどうかは不明ですが、一貫している必要があります。

うまくいけば、これが役に立ちます。

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

第2に、マイグレーション/シードファイルが自然な順序で選択されていることを考慮する必要があります。あなたの場合、comments.jsファイルがposts.jsファイルよりも早く実行されていることが原因で、挿入が失敗した原因が明白です。

あなたは post.js 20170812_20_comments.jsから 20170812_10_posts.jsへと comments.js(e..gのために)名前を変更することがあり、この問題を解決するには。プレフィックス <date>_<seq>_は単なる推奨された慣例にすぎません。その名前が意図した順序である限り、移行/シードファイルの命名中に任意の規則に従うことができます。

関連の問題:このことができます https://github.com/tgriesser/knex/issues/993

期待しています。