Polymorphic association in Sequelize with migration

Basic models
Image.hasMany(Comment)
Video.hasMany(Comment)
Comment.belongsTo(Image)
Comment.belongsTo(Video)
Multiple hasMany associations
  • commentableId : videoId or imageId
  • commentableType: video or image
//video model association
Video.hasMany(models.Comment, {
foreignKey: 'commentableId',
constraints: false,
scope: {
commentableType: 'video'
}
});
//image model association
Image.hasMany(models.Comment, {
foreignKey: 'commentableId',
constraints: false,
scope: {
commentableType: 'image'
}
});
//comment model association
Comment.belongsTo(models.Image, {
foreignKey: 'commentableId',
constraints: false
});
Comment.belongsTo(models.Video, {
foreignKey: 'commentableId',
constraints: false
});
const image = await Image.findByPk(1)
// Sequelize creates a new comment with commentableType as image and commentableId as 1
await image.createComment({title:'Believe in magic'})
// to get all comments belonging to this image.
await image.getComments()
getCommentable(options) {
if (!this.commentableType) return Promise.resolve(null);
const mixinMethodName = `get${uppercaseFirst(this.commentableType)}`;
return this[mixinMethodName](options);
}
const comment1 = await Comment.findByPk(1);
const videoOrImage = comment1.getCommentable();
//Added to prevent duplicate issues during eager loading.
Comment.addHook("afterFind", findResult => {
if (!Array.isArray(findResult)) findResult = [findResult];
for (const instance of findResult) {
if (instance.commentableType === "image" && instance.Image !== undefined) {
instance.commentable = instance.Image;
} else if (instance.commentableType === "video" && instance.Video !== undefined) {
instance.commentable = instance.Video;
}
// delete to prevent duplicates
delete instance.Image;
delete instance.dataValues.Image;
delete instance.Video;
delete instance.dataValues.Video;
}
});
const comments = await Comment.findAll({
include: [Image, Video]
});
for (const comment of comments) {
const message = `Found comment #${comment.id} with ${comment.commentableType} commentable:`;
console.log(message, JSON.stringify(comment.commentable));
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store