当前位置: 首页 > 后端技术 > Node.js

Sequelize多态关联学习记录

时间:2023-04-03 23:18:52 Node.js

在学习Sequelize的时候,我做了一个小笔记,分享一下这部分的理解,方便其他有相同需求的朋友查阅,少走弯路。多态关联由使用相同外键发生的两个(或更多)关联组成。例如:考虑模型文章、视频、图像和评论。前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//主键,由数据库生成'视频'|'image'//typetitle:String//commentcontent//otherfielddefinitions...}以下是根据官网文档advancedassociation状态下的很多association,通过自己的DEMO实践稍作修改。这是代码的基本框架:const{Sequelize,Op,Model,DataTypes,QueryTypes}=require('sequelize')constsequelize=newSequelize('test','root','xx',{dialect:'mysql',host:'localhost',logging:false,port:3306,timezone:'+08:00',});(async()=>{try{awaitsequelize.authenticate()console.log('连接已经建立成功。')}catch(error){console.error('Unabletoconnecttothedatabase:',error)}})();//表关系定义写在这里(async()=>{awaitsequelize.sync({alter:true})//操作代码在这里...})();//方便重置数据库表//(async()=>{//awaitsequelize.drop()//})()下面是实现代码(不能直接运行,需要放在合适的位置)constuppercaseFirst=str=>`${str[0].toUpperCase()}${str.substr(1)}`;constArticle=sequelize.define('article',{title:DataTypes.STRING,content:DataTypes.TEXT});constImage=sequelize.define('image',{title:DataTypes.STRING,url:DataTypes.STRING});constVideo=sequelize.define('video',{title:DataTypes.STRING,text:DataTypes.STRING});constComment=sequelize.define('comment',{title:DataTypes.STRING,commentId:DataTypes.INTEGER,commentType:DataTypes.STRING});//获取打包后的评论数据Comment.prototype.getCommentDataValue=function(options){if(!this.commentType)returnPromise.resolve(null);constmixinMethodName=`get${uppercaseFirst(this.commentType)}`;returnthis[mixinMethodName](options);};Image.hasMany(Comment,{foreignKey:'commentId',约束:false,scope:{commentType:'image'}});Comment.belongsTo(Image,{foreignKey:'commentId',constraints:false});Article.hasMany(Comment,{foreignKey:'commentId',constraints:false,scope:{commentType:'article'}});Comment.belongsTo(文章,{foreignKey:'commentId',constraints:false});Video.hasMany(Comment,{foreignKey:'commentId',constraints:false,scope:{commentType:'video'}});Comment.belongsTo(Video,{foreignKey:'commentId',约束:错误的});//为了防止预加载错误/错误,在同一个afterFind钩子中从Comment实例中删除具体字段,只留下抽象的commentDataValue字段可用。Comment.addHook("afterFind",findResult=>{//关联模型,实际项目配置constcommentTypes=['article','image','video'];if(!Array.isArray(findResult))findResult=[findResult];for(constinstanceoffindResult){for(consttypeofcommentTypes){if(instance.commentType===type){if(instance[type]!==undefined){//存储处理后的数据instance.commentDataValue=instance[type]}else{instance.commentDataValue=instance[`get${uppercaseFirst(type)}`]()}}}//防止错误:for(consttypeofcommentTypes){deleteinstance[type]deleteinstance.dataValues[type]}}});接下来,简单地添加2条数据constimage=awaitImage.create({title:'Image',url:"https://placekitten.com/408/287"});constcomment=awaitimage.createComment({title:"Awesome!"});const文章=等待文章。创建({标题:'文章标题',content:'文章内容'})constcomment=awaitarticle.createComment({title:"文章写得很好!"});数据库评论表数据如下:idtitlecommentIdcommentTypecreatedAtupdatedAt1Awesome!1image2021-09-1815:20:292021-09-1815:20:292文章写得好!1article2021-09-1815:20:292021-09-1815:20:29数据库articles表数据如下:idtitlecontentcreatedAtupdatedAt1articletitlearticlecontent2021-09-1815:20:292021-09-1815:20:29数据库图片表数据如下:idtitleurlcreatedAtupdatedAt1张图片https://placekitten.com/408/2872021-09-1815:20:292021-09-1815:20:29操作示例:查询图片评论constimage=awaitImage.findByPk(1)//结果//{//"id":1,//"title":"image",//"url":"https://placekitten.com/408/287",//"createdAt":"2021-09-18T07:20:29.000Z",//"updatedAt":"2021-09-18T07:20:29.000Z"//}awaitimage.getComments()//[//{//"id":1,//"title":"Awesome!",//"commentId":1,//"commentType":"image",//"createdAt":"2021-09-18T07:20:29.000Z",//"updatedAt":"2021-09-18T07:20:29.000Z"//}//]根据ID查询评论constccomment=awaitComment.findByPk(1)//结果//{//"id":1,//"title":"Awesome!",//"commentId":1,//"commentType":"image",//"createdAt":"2021-09-18T07:20:29.000Z",//"updatedAt":"2021-09-18T07:20:29.000Z"//}等待评论.getCommentDataValue()等待评论.commentDataValue//或//结果//{//"id":1,//"title":"Image",//"url":"https://placekitten.com/408/287",//"createdAt":"2021-09-18T07:20:29.000Z",//"updatedAt":"2021-09-18T07:20:29.000Z"//}查询所有评论,因为没有约束,关联Model数据需要自己处理,在这个选项中使用include没有效果constcomments=awaitComment.findAll()//Result//[//{//"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":"文章",//"创建时间":"2021-09-18T07:20:29.000Z",//"更新时间":"2021-09-18T07:20:29.000Z"//}//]查询所有评论和关联模型constresult=[]for(constcommentofcomments){//传入选项过滤数据comment.dataValues[comment.commentType]=awaitcomment.getCommentDataValue({//注意这里的值要根据`comment.commentType`来区分,不同的模型字段是不同的属性:['title']})//或者直接获取所有数据comment.dataValues[comment.commentType]=awaitcomment.commentDataValueresult.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":"Image",//"url":"https://placekitten.com/408/287",//"createdAt":"2021-09-18T07:20:29.000Z",//"updatedAt":"2021-09-18T07:20:29.000Z"//}//},//{//"id":2,//"标题":"文章写的很好!",//"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"//}//}//]最后,如果有什么好的做法,希望大家留言一起学习讨论,让学习互相促进