How to implement many to many association in sequelize How to implement many to many association in sequelize express express

How to implement many to many association in sequelize


Sequelize Association Cheatsheet

Updated for Sequelize v2/3/4/5

Generally I think the problems are that we are confused about what tables created, and what methods are gained by associations.

Note: Defining foreignKey or cross table name are optional. Sequelize automatically creates it, but defining it allows coders to read the models and find out what the foreign keys/cross table names are, instead of guessing or needing to access the database.

TLDR;

O:O

// foreign key has to be defined on both sides.Parent.hasOne(Child, {foreignKey: 'Parent_parentId'})// "Parent_parentId" column will exist in the "belongsTo" table.Child.belongsTo(Parent, {foreignKey: 'Parent_parentId'})

O:M

Parent.hasMany(Child, {foreignKey: 'Parent_parentId'})Child.belongsTo(Parent, {foreignKey: 'Parent_parentId'})

N:M

Parent.belongsToMany(    Child,     {        // this can be string (model name) or a Sequelize Model Object Class        // through is compulsory since v2        through: 'Parent_Child',        // GOTCHA        // note that this is the Parent's Id, not Child.         foreignKey: 'Parent_parentId'    })/*The above reads:"Parents" belongs to many "Children", and is recorded in the "Parent_child" table, using "Parents"'s ID.*/Child.belongsToMany(    Parent,     {        through: 'Parent_Child',        // GOTCHA        // note that this is the Child's Id, not Parent.        foreignKey: 'Child_childId'    })

Why the verbose "Parent_parentId" and not just "parentId"? This is to make it obvious that it's a foreign key that belonged to "Parent". In most cases it's okay to just use the more succinct "parentId".*

Associations gives you 2 functionality: (1) Eager loading and (2) DAO Methods:

1. Include (Eager loading)

DB.Parent.findOne({     where: { id: 1 },    include: [ DB.Child ]}).then(parent => {    // you should get `parent.Child` as an array of children. })

2. Methods gained by hasOne(), hasMany() and belongsTo()/belongsToMany()

Associations give the Data Access Object (DAO) methods:

hasOne():

In setting a Parent.hasOne(Child), methods available to parent DAO instance:

DB.Parent.findOne({ where: { id: 1 } }).then(parent => {    // `parent` is the DAO    // you can use any of the methods below:    parent.getChild    parent.setChild    parent.addChild    parent.createChild    parent.removeChild    parent.hasChild})
hasMany():

In setting a Parent.hasMany(Child), methods available to parent DAO instance:

parent.getChildren,parent.setChildren,parent.addChild,parent.addChildren,parent.createChild,parent.removeChild,parent.hasChild,parent.hasChildren,
belongsTo()/belongsToMany:

In setting a Child.belongsTo(Parent), methods available to child DAO instance:

child.getParent,child.setParent,child.createParent,//belongsToManychild.getParents,child.setParents,child.createParents,

You can also have multiple relationships

Natural Parents/Children
// a parent can have many childrenParent.belongsToMany(Child, {    as: 'Natural',    through: 'Parent_Child',    foreignKey: 'Parent_parentId'})// a child must at least have 2 parents (natural mother and father)Child.belongsToMany(Parent, {    as: 'Natural',    through: 'Parent_Child',    foreignKey: 'Child_childId'})
Foster Parents/Children
Parent.belongsToMany(Child, {    as: 'Foster',    through: 'Parent_Child',    foreignKey: 'Parent_parentId'})Child.belongsToMany(Parent, {    as: 'Foster',    through: 'Parent_Child',    foreignKey: 'Child_childId'});

The above will create the Parent_Child cross table, with NaturalId and FosterId.


delete BookArticles model and update relation to:

m.Book.hasMany(m.Article, {through: 'book_articles'});m.Article.hasMany(m.Books, {through: 'book_articles'});


This is how i solved the similar problem i had two models a user model

var user = sequelize.define('user', {    name: {        Sequelize.STRING(255)    },    email: {        type: Sequelize.STRING(255),        unique: true,        validate: {            isEmail: true        }    }});

and a roles model

var Role = sequelize.define('role', {    name: {        Sequelize.ENUM('ER', 'ALL', 'DL')    },    description: {        type: Sequelize.TEXT    }});

Then i created the union model UserRole

var UserRole = sequelize.define('user_role', {    id: {        type: Sequelize.INTEGER,        primaryKey: true,        autoIncrement: true    },    name: {        type: Sequelize.ENUM('Admin', 'Staff', 'Customer', 'Owner')    }});

Note: you have to explicitly define the id for UserRole otherwise sequelize will use the two foreign keys in this case user_id and role_id as your primary keys.

Then i created the belongs to many relationship as follows

User.belongsToMany(Role, { as: 'Roles', through: { model: UserRole, unique: false }, foreignKey: 'user_id' });Role.belongsToMany(User, { as: 'Users', through: { model: UserRole, unique: false }, foreignKey: 'role_id' });