5
votes

I'm new in this sequelize things. I try to associate model between Users and Permissions through UserPermissions using belongsToMany, and this is my code.

-- users.js

const bcrypt = require('bcrypt');
const config = require('../config/general');

module.exports = (sequelize, DataTypes) => {
    const User = sequelize.define('User', {
        email: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true,
            validate: {
                isLowercase: true,
                notEmpty: true,
                isEmail: true
            }
        },
        username: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true,
            validate: {
                isLowercase: true,
                notEmpty: true,
                min: 3
            }
        },
        salt: {
            type: DataTypes.STRING,
            allowNull: true
        },
        password: {
            type: DataTypes.STRING,
            allowNull: false,
            validate: {
                notEmpty: true
            }
        },
    }, {
        underscored: true,
        classMethods: {
            associate: (models) => {
                User.belongsToMany(models.Permission, {
                    through: 'UserPermissions', 
                    foreignKey: 'user_id'
                });
            },
            validPassword: (password, passwd, done) => {
                const tmppass = password + config.secret;
                bcrypt.compare(tmppass, passwd, (err, isMatch) => {
                    if (err) return done(err);
                    return done(null, isMatch);
                });
            }
        }
    });

    User.beforeCreate( (user, option, done) => {
        bcrypt.genSalt(SALT_WORK_FACTOR, (err, salt) => {
            if (err) return done(err);
            const tmppass = user.password + config.secret;
            bcrypt.hash(tmppass, salt, (err, hash) => {
                if (err) return done(err);
                user.salt       = salt;
                user.password   = hash;
                return done(null, user);
            });
        });
    });

    return User;
};

-- permissions.js

module.exports = (sequelize, DataTypes) => {
    const Permission = sequelize.define('Permission', {
        name: {
            type: DataTypes.STRING,
            allowNull: false,
            validate: {
                notEmpty: true
            }
        },
        slug: {
            type: DataTypes.STRING,
            validate: {
                isLowercase: true
            }
        },
        description: {
            type: DataTypes.TEXT
        }
    }, {
        underscored: true,
        classMethods: {
            associate: (models) => {
                Permission.belongsToMany(models.User, { 
                    through: 'UserPermissions', 
                    foreignKey: 'permission_id'
                });
            }
        }
    });

    return Permission;
};

According to sequelize docs about belongsToMany in here, belongsToMany will create a new model that link to whatever model that you join, right.

This will create a new model called UserProject with the equivalent foreign keys projectId and userId. Whether the attributes are camelcase or not depends on the two models joined by the table (in this case User and Project). Sequelize Belongs-To-Many

But when I try that and migrate it using sequelize-cli, I didn't see any join table that had been created. The Users Table is created, The Permissions Table is created, but UserPermissions Table is not created. Did I miss some thing in here? or there is something wrong with my code?

I'm using postgres dialect, "pg": "^6.4.0" and "sequelize": "^4.3.1"

Oh ya, I'm really sorry about my english, I'm not really good in english.

1
You will need to write your own migration for the through tablemcranston18
Ah I see, so that's mean this belongsToMany didn't create is own join table if we use migration?Lukman Wahyudi
Correct. If you create a belongsToMany relationship, you will need to manually create a migration file and run your migrations.mcranston18

1 Answers

7
votes

To answer your question (many years later):

You were likely told by the documentation to do this to create a join table:

In your User associations:

User.belongsToMany(models.Permission, {
                    through: 'UserPermissions', 
                    foreignKey: 'user_id'
                });

In your Permission associations:

Permission.belongsToMany(models.User, { 
                    through: 'UserPermissions', 
                    foreignKey: 'permission_id'
                });

The portion of the process not mentioned in the documentation is how to actually create this table that you use in the database. To create the table used by these functions in the database, create a migration with the following code:

module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.createTable('UserPermissions', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      permission_id: {
        type: Sequelize.INTEGER,
        references: {
          model: 'Permissions',
          key: 'id',
          as: 'permission_id'
        }
      },
      user_id: {
        type: Sequelize.INTEGER,
        references: {
          model: 'Users',
          key: 'id',
          as: 'user_id'
        }
      },
      createdAd: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    })
  },
  down: (queryInterface, Sequelize) => {
    return queryInterface.dropTable('UserPermissions')
  }
}

Using the Sequelize CLI will generate most of this code for you. Note that the model object property under references is the table name in postgres (well, this is what worked for me).

EDIT:

Also note that the through property should be a model, so:

through: models.UserPermission

and create the relevant model.