关于前端:Sequelize-多态关联学习记录

3次阅读

共计 5673 个字符,预计需要花费 15 分钟才能阅读完成。

学习 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 表数据如下:

id title commentId commentType createdAt updatedAt
1 Awesome! 1 image 2021-09-18 15:20:29 2021-09-18 15:20:29
2 文章写得不错! 1 article 2021-09-18 15:20:29 2021-09-18 15:20:29

数据库 articles 表数据如下:

id title content createdAt updatedAt
1 文章题目 文章内容 2021-09-18 15:20:29 2021-09-18 15:20:29

数据库 images 表数据如下:

id title url createdAt updatedAt
1 图片 https://placekitten.com/408/287 2021-09-18 15:20:29 2021-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"
//     }
//   }
// ]

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

正文完
 0