Monday 9 November 2020

Adding Attributes on a Join Table in Sequelize

I'm having trouble setting an attribute on a junction table.

I have a Many-to-Many association defined between two models UserModel and HangModel, through a custom table HangUsers.

const HangModel = rootRequire('/models/Hang');
const UserModel = rootRequire('/models/User');

const HangUsers = database.define('HangUsers', {
  id: {
    type: Sequelize.INTEGER(10).UNSIGNED,
    primaryKey: true,
    autoIncrement: true,
  },
  hangId: {
    type: Sequelize.INTEGER(10).UNSIGNED,
    references: {
      model: HangModel,
      key: 'id',
    },
  },
  userId: {
    type: Sequelize.INTEGER(10).UNSIGNED,
    references: {
      model: UserModel,
      key: 'id',
    },
  },
  rsvp: {
    type: Sequelize.STRING,
    allowNull: false,
    validate: {
      isIn: {
        args: [ 'pending', 'joined' ],
        msg: 'The rsvp provided is invalid',
      },
    },
  },
});

UserModel.hasMany(HangUsers, { as: 'invitations' });
HangModel.hasMany(HangUsers, { as: 'invites' });

UserModel.belongsToMany(HangModel, { through: HangUsers });
HangModel.belongsToMany(UserModel, { through: HangUsers });

The through table has a column rsvp, that I'm trying to populate when I add users to a hang:

const hang = await HangModel.create();
await hang.addUser(user, { through: { rvsp: 'joined' } });

However, I'm getting an error:

AggregateError
    at recursiveBulkCreate (/Users/sgarza62/ditto-app/api/node_modules/sequelize/lib/model.js:2600:17)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async Function.bulkCreate (/Users/sgarza62/ditto-app/api/node_modules/sequelize/lib/model.js:2824:12)
    at async Promise.all (index 0)
    at async BelongsToMany.add (/Users/sgarza62/ditto-app/api/node_modules/sequelize/lib/associations/belongs-to-many.js:740:30)
    at async /Users/sgarza62/ditto-app/api/routes/hangs.js:121:3 {
  name: 'AggregateError',
  errors: [
    BulkRecordError [SequelizeBulkRecordError]: notNull Violation: HangUsers.rsvp cannot be null
        at /Users/sgarza62/ditto-app/api/node_modules/sequelize/lib/model.js:2594:25
        at processTicksAndRejections (internal/process/task_queues.js:97:5) {
      name: 'SequelizeBulkRecordError',
      errors: [ValidationError],
      record: [HangUsers]
    }
  ]
}

When I allow null on the rsvp column, the HangUsers row is created, but the rsvp value is NULL.

It seems the { through: { rsvp: 'joined' } } parameter is being ignored.

I've done this all according to the BelongsToMany docs and the Advanced M:N Associations docs, where it says:

However, defining the model by ourselves has several advantages. We can, for example, define more columns on our through table:

const User_Profile = sequelize.define('User_Profile', {
  selfGranted: DataTypes.BOOLEAN
}, { timestamps: false });
User.belongsToMany(Profile, { through: User_Profile });
Profile.belongsToMany(User, { through: User_Profile });

With this, we can now track an extra information at the through table, namely the selfGranted boolean. For example, when calling the user.addProfile() we can pass values for the extra columns using the through option.

Example:

const amidala = await User.create({ username: 'p4dm3', points: 1000 });
const queen = await Profile.create({ name: 'Queen' });
await amidala.addProfile(queen, { through: { selfGranted: false } });
const result = await User.findOne({
  where: { username: 'p4dm3' },  
  include: Profile
});
console.log(result);


from Adding Attributes on a Join Table in Sequelize

No comments:

Post a Comment