种子外键约束
我想在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)
是我的外键约束错误?
任何建议为什么我得到这个错误?
我感谢您的回复!
在迁移创建方面,尝试分离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
,满足约束条件。
从我所看到的将是如下播种顺序:
- 删除评论
- 删帖
- 创建的帖子
- 使用有效的意见创建/现有职位的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
,但它需要保持一致。
希望这有助于!
看起来您需要更正您的评论表映射,如下所示,以在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.js
到20170812_10_posts.js
和comments.js
到20170812_20_comments.js
(对于e..g)。前缀<date>_<seq>_
仅仅是一个建议的惯例。只要命名迁移/种子文件的名称符合自然顺序,您可以遵循任何惯例。
相关的问题: https://github.com/tgriesser/knex/issues/993
希望这有助于。