使用Sequelize进行多对多关系的简单示例

我正在尝试使用Sequelize构build表之间多对多关系的简单示例。 不过,这似乎比我预料的要棘手。

这是我目前的代码( ./db.js文件导出Sequelize连接实例)。

 const Sequelize = require("sequelize"); const sequelize = require("./db"); var Mentee = sequelize.define('mentee', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, name: { type: Sequelize.STRING } }); var Question = sequelize.define('question', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, text: { type: Sequelize.STRING } }); var MenteeQuestion = sequelize.define('menteequestion', { // answer: { // type: Sequelize.STRING // } }); // A mentee can answer several questions Mentee.belongsToMany(Question, { as: "Questions", through: MenteeQuestion }); // And a question can be answered by several mentees Question.belongsToMany(Mentee, { as: "Mentees", through: MenteeQuestion }); let currentQuestion = null; Promise.all([ Mentee.sync({ force: true }) , Question.sync({ force: true }) , MenteeQuestion.sync({ force: true }) ]).then(() => { return Mentee.destroy({where: {}}) }).then(() => { return Question.destroy({ where: {} }) }).then(() => { return Question.create({ text: "What is 42?" }); }).then(question => { currentQuestion = question; return Mentee.create({ name: "Johnny" }) }).then(mentee => { console.log("Adding question"); return mentee.addQuestion(currentQuestion); }).then(() => { return MenteeQuestion.findAll({ where: {} , include: [Mentee] }) }).then(menteeQuestions => { return MenteeQuestion.findAll({ where: { menteeId: 1 } , include: [Mentee] }) }).then(menteeQuestion => { console.log(menteeQuestion.toJSON()); }).catch(e => { console.error(e); }); 

运行时,我得到:

无法添加外键约束

我认为这是因为idtypes – 但是我不知道为什么会出现,以及我们如何解决它。

前一个不会出现的另一个错误是:

执行(默认): INSERT INTO menteequestionsmenteeIdquestionIdupdatedAtupdatedAt )VALUES(2,1,'2017-03-17 06:18:01','2017-03-17 06:18:01');

错误:mentee没有关联menteequestion!

另外,我得到的另一个错误 – 我认为这是因为force:truesync -is:

DROP TABLE IF EXISTS mentees ;

ER_ROW_IS_REFERENCED:无法删除或更新父行:外键约束失败

如何解决这些?

再次,我只需要一个多对多的crud操作的简单例子(在这种情况下只是插入和阅读),但是这似乎超出了我的理解。 用这个挣扎了两天。

迁移

我build议你使用sequelize 迁移,而不是在每个模型上执行sync() 。 有一个模块 – sequelize.cli ,可以让你轻松pipe理迁移和种子。 它以某种方式通过在项目的/models目录中创build初始化文件index.js来强制项目结构。 它假定所有的模型定义都在这个目录下。 这个脚本遍历所有的模型文件(每个模型定义在单独的文件中,例如mentee.jsquestion.js ),并执行sequelize.import()以将这些模型分配给续集实例 – 这可以让您稍后访问它们sequelize[modelName]例如sequelize.question

注意:创build迁移文件时,请记住时间戳字段 – updatedAtupdatedAt和最终的deletedAt

同步

我个人使用sync()只有当我运行testing – 这可能会显示在三个步骤

  1. 执行sequelize.sync({ force: true })以同步所有模型
  2. 运行一些数据库seeds (也可以通过sequelize-cli ),
  3. 运行testing。

这是非常舒适的,因为允许您在运行testing之前清理数据库,并且为了区分开发和testing,testing可以使用不同的数据库,例如project_test ,以便开发数据库保持不变。

许多一对多

现在让我们继续讨论两个模型之间的问题 – m:n关系。 首先,由于您执行Promise.all() ,所以sync可以以不同于添加其中的函数的顺序运行。 为了避免这种情况,我build议你使用Bluebird承诺的mapSeries特性,sequelize在sequelize.Promise下使用和公开。(这也是你最后一次删除父行的错误的原因 – 你试图删除被menteequestion )。

 sequelize.Promise.mapSeries([ Mentee.sync({ force: true }) , Question.sync({ force: true }) , MenteeQuestion.sync({ force: true }) ], (model) => { return model.destroy({ where: {} }); }).then(() => { }); 

mapSeries第一个参数是mapSeries的数组,然而第二个参数是一个函数,它是以前面定义的promise的结果运行的。 由于Model.sync()导致模型本身的事实,我们可以在每次迭代中执行model.destroy()

之后,您可以通过create()将一些数据插入数据库,就像在这个例子中一样。 现在时间来解决错误:mentee不关联menteequestion! 错误。 它发生是因为你有与Question相关的MenteeQuestion ,但MenteeQuestionMenteeQuestion (或Question )之间没有关联。 为了解决这个问题,在belongsToMany之后,你可以添加

 MenteeQuestion.belongsTo(Mentee, { foreignKey: 'menteeId' }); MenteeQuestion.belongsTo(Question, { foreignKey: 'questionId' }); 

现在您可以在查询MenteeQuestion时添加include: [Mentee, Question] MenteeQuestion include: [Mentee, Question] 。 在执行toJSON() ,你也会运行另一个错误,因为你确实find了返回实例数组的所有东西。 你可以做forEach()

 menteeQuestions.forEach(menteeQuestion => { console.log(menteeQuestion.toJSON()); });