学习 Sequelize 时对这部分了解作一个小小的笔记分享进去,不便查阅和其余须要同样需要的小伙伴少走弯路。

一个 多态关联 由应用同一外键产生的两个(或多个)关联组成.

例如:思考模型 Article, Video, ImageComment. 前3个代表用户可能公布的内容. 咱们心愿3者都领有评论,咱们能够这样去定义关系:

Article.hasMany(Comment)Comment.belongsTo(Article)Video.hasMany(Comment)Comment.belongsTo(Video)Image.hasMany(Comment)Comment.belongsTo(Image)

下面的形式会导致在 Comment 表上创立3个外键 articleId, videoId, imageId. 这很显然很麻烦,冗余,更好的方法是实现上面的表构造:

{  id: Number // 主键,由数据库生成  commentId: Number // 外键,对应 articleId/videoId/imageId 其中一个  commentType: 'article' | 'video' | 'image' // 类型  title: String // 评论内容  // 其它字段定义...}

上面是依据官方网站文档高级关联状态中的多太关联局部通过本人DEMO实际小改而来。

上面是代码的根本架子:

const { Sequelize, Op, Model, DataTypes, QueryTypes } = require('sequelize')const sequelize = new Sequelize( 'test', 'root', 'xx', {  dialect: 'mysql',  host: 'localhost',  logging: false,  port: 3306,  timezone: '+08:00',});(async () => {  try {    await sequelize.authenticate()    console.log( 'Connection has been established successfully.' )  } catch ( error ) {    console.error( 'Unable to connect to the database:', error )  }})();// 表关系定义写这儿(async () => {  await sequelize.sync({ alter: true })  // 操作代码写这儿...})();// 不便重置数据库表// (async () => {//   await sequelize.drop()// })()

上面是实现代码(不能间接运行,须要搁置在适合的地位)

const uppercaseFirst = str => `${str[0].toUpperCase()}${str.substr(1)}`;const Article = sequelize.define('article', {  title: DataTypes.STRING,  content: DataTypes.TEXT});const Image = sequelize.define('image', {  title: DataTypes.STRING,  url: DataTypes.STRING});const Video = sequelize.define('video', {  title: DataTypes.STRING,  text: DataTypes.STRING});const Comment = sequelize.define('comment', {  title: DataTypes.STRING,  commentId: DataTypes.INTEGER,  commentType: DataTypes.STRING});// 获取包装后的评论数据Comment.prototype.getCommentDataValue = function(options) {  if (!this.commentType) return Promise.resolve(null);  const mixinMethodName = `get${uppercaseFirst(this.commentType)}`;  return this[mixinMethodName](options);};Image.hasMany(Comment, {  foreignKey: 'commentId',  constraints: false,  scope: {    commentType: 'image'  }});Comment.belongsTo(Image, { foreignKey: 'commentId', constraints: false });Article.hasMany(Comment, {  foreignKey: 'commentId',  constraints: false,  scope: {    commentType: 'article'  }});Comment.belongsTo(Article, { foreignKey: 'commentId', constraints: false });Video.hasMany(Comment, {  foreignKey: 'commentId',  constraints: false,  scope: {    commentType: 'video'  }});Comment.belongsTo(Video, { foreignKey: 'commentId', constraints: false });// 为了避免事后加载的 bug/谬误, 在雷同的 afterFind hook 中从 Comment 实例中删除具体字段,仅保留形象的 commentDataValue 字段可用.Comment.addHook("afterFind", findResult => {  // 关联的模型,理论我的项目走配置  const commentTypes = ['article', 'image', 'video'];  if (!Array.isArray(findResult)) findResult = [findResult];  for (const instance of findResult) {    for (const type of commentTypes) {      if (instance.commentType === type) {        if (instance[type] !== undefined) {          // 寄存解决后的数据          instance.commentDataValue = instance[type]        } else {          instance.commentDataValue = instance[`get${uppercaseFirst(type)}`]()        }      }    }    // 避免谬误:    for (const type of commentTypes) {      delete instance[type]      delete instance.dataValues[type]    }  }});

接下来简略增加2条数据

const image = await Image.create({ title: '图片', url: "https://placekitten.com/408/287" });const comment = await image.createComment({ title: "Awesome!" });const article = await Article.create({ title: '文章题目', content: '文章内容' })const comment = await article.createComment({ title: "文章写得不错!" });

数据库 comments 表数据如下:

idtitlecommentIdcommentTypecreatedAtupdatedAt
1Awesome!1image2021-09-18 15:20:292021-09-18 15:20:29
2文章写得不错!1article2021-09-18 15:20:292021-09-18 15:20:29

数据库 articles 表数据如下:

idtitlecontentcreatedAtupdatedAt
1文章题目文章内容2021-09-18 15:20:292021-09-18 15:20:29

数据库 images 表数据如下:

idtitleurlcreatedAtupdatedAt
1图片https://placekitten.com/408/2872021-09-18 15:20:292021-09-18 15:20:29

操作示例:

  • 查问图片评论
const image = await Image.findByPk(1)// 后果// {//   "id": 1,//   "title": "图片",//   "url": "https://placekitten.com/408/287",//   "createdAt": "2021-09-18T07:20:29.000Z",//   "updatedAt": "2021-09-18T07:20:29.000Z"// }await image.getComments()// [//   {//     "id": 1,//     "title": "Awesome!",//     "commentId": 1,//     "commentType": "image",//     "createdAt": "2021-09-18T07:20:29.000Z",//     "updatedAt": "2021-09-18T07:20:29.000Z"//   }// ]
  • 依据ID查问评论
const comment = await Comment.findByPk(1)// 后果// {//   "id": 1,//   "title": "Awesome!",//   "commentId": 1,//   "commentType": "image",//   "createdAt": "2021-09-18T07:20:29.000Z",//   "updatedAt": "2021-09-18T07:20:29.000Z"// }await comment.getCommentDataValue()await comment.commentDataValue // or// 后果// {//   "id": 1,//   "title": "图片",//   "url": "https://placekitten.com/408/287",//   "createdAt": "2021-09-18T07:20:29.000Z",//   "updatedAt": "2021-09-18T07:20:29.000Z"// }
  • 查问所有评论

因为没有了束缚,所关联的模型数据须要自行处理,这里选项中应用 include 不起任何作用

const comments = await Comment.findAll()// 后果// [//   {//     "id": 1,//     "title": "Awesome!",//     "commentId": 1,//     "commentType": "image",//     "createdAt": "2021-09-18T07:20:29.000Z",//     "updatedAt": "2021-09-18T07:20:29.000Z"//   },//   {//     "id": 2,//     "title": "文章写得不错!",//     "commentId": 1,//     "commentType": "article",//     "createdAt": "2021-09-18T07:20:29.000Z",//     "updatedAt": "2021-09-18T07:20:29.000Z"//   }// ]
  • 查问所有评论并关联模型
const result = []for (const comment of comments) {  // 传入选项过滤数据  comment.dataValues[comment.commentType] = await comment.getCommentDataValue({    // 留神,这里的值要依据 `comment.commentType` 来辨别,不同的模型字段不一样    attributes: [      'title'    ]  })  // or 间接获取所有数据  comment.dataValues[comment.commentType] = await comment.commentDataValue  result.push(comment.dataValues)}// 后果// [//   {//     "id": 1,//     "title": "Awesome!",//     "commentId": 1,//     "commentType": "image",//     "createdAt": "2021-09-18T07:20:29.000Z",//     "updatedAt": "2021-09-18T07:20:29.000Z",//     "image": {//       "id": 1,//       "title": "图片",//       "url": "https://placekitten.com/408/287",//       "createdAt": "2021-09-18T07:20:29.000Z",//       "updatedAt": "2021-09-18T07:20:29.000Z"//     }//   },//   {//     "id": 2,//     "title": "文章写得不错!",//     "commentId": 1,//     "commentType": "article",//     "createdAt": "2021-09-18T07:20:29.000Z",//     "updatedAt": "2021-09-18T07:20:29.000Z",//     "article": {//       "id": 1,//       "title": "文章题目",//       "content": "文章内容",//       "createdAt": "2021-09-18T07:20:29.000Z",//       "updatedAt": "2021-09-18T07:20:29.000Z"//     }//   }// ]

最初,如果有什么好的实际还心愿留言一起学习探讨一下,学习互相促进。