asanas
asanas

Reputation: 4280

Work around Sequelize’s unique constraints in belongsToMany associations

I'm using Sequelize in my project. These are the two models:

const User = db.define('user', {
  name: Sequelize.STRING,
  password: Sequelize.STRING
})
const Product = db.define('product', {
  name: Sequelize.STRING,
  price: Sequelize.INTEGER
})

Now users can purchase products and I have associations setup like below:

Product.belongsToMany(User, {through: 'UserProducts'})
User.belongsToMany(Product, {through: 'UserProducts'})

I also have this UserProducts table with an additional column.

const UserProducts = db.define('UserProducts', {
  status: Sequelize.STRING
})

Sequelize creates a composite key with combination of userId and productId and will only allow one record with a combination of userId and productId. So, for example, userId 2 and productId 14.

This is a problem for me because sometimes people want to purchase multiple times. I need one of the following scenarios to work:

  1. Don't use the composite key and instead have a completely new auto-increment column used as key in UserProducts.

  2. Instead of making key with userId and productId alone, allow me to add one more column into the key such as the status so that unique key is a combination of the three.

I do want to use the associations as they provide many powerful methods, but want to alter the unique key to work in such a way that I can add multiple rows with the same combination of user id and product id.

And since my models/database is already running, I will need to make use of migrations to make this change.

Any help around this is highly appreciated.

Upvotes: 8

Views: 4903

Answers (3)

Matthieu Alcaro
Matthieu Alcaro

Reputation: 41

Since setting explicitly the unique property on the through model is not working in v6, the only solution i found is to define the 3 parts of the association this way :

User.hasMany(UserProducts);
UserProducts.belongsTo(User);

Product.hasMany(UserProducts);
UserProducts.belongsTo(Product);

You can then create your models and associations :

const user = await User.create(user_data);
const product = await Product.create(product_data);
const up = await UserProduct.create(up_data);
await up.setUser(user);
await up.setProduct(product);

If anyone has a better solution, I would be happy to know it

Upvotes: 0

Neo_
Neo_

Reputation: 282

If anyone else is having problems in v5 of Sequelize, it is not enough to specify a primary key on the 'through' model.
You have to explicitly set the unique property on the through model.

User.belongsToMany(Product, { through: { model: UserProducts, unique: false } });
Product.belongsToMany(User, { through: { model: UserProducts, unique: false } });

Upvotes: 18

Aditya Kokil
Aditya Kokil

Reputation: 423

Belongs-To-Many creates a unique key when the primary key is not present on through model.

Since you also have additional property in your UserProducts table, you need to define the model for UserProducts and then tell the sequelize to use that model instead of creating its own

class User extends Model {}
User.init({
    name: Sequelize.STRING,
    password: Sequelize.STRING
}, { sequelize })

class Product extends Model {}
ProjProductect.init({
    name: Sequelize.STRING,
    price: Sequelize.INTEGER
}, { sequelize })

class UserProducts extends Model {}
UserProducts.init({
  id: {
    type: Sequelize.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  status: DataTypes.STRING
}, { sequelize })

User.belongsToMany(Project, { through: UserProducts })
Product.belongsToMany(User, { through: UserProducts })

refer: Sequelize v4 belongsToMany

UPDATE

since you are using v3 and already have a model created for your UserProducts table use following snippet

UserProducts = db.define('UserProducts', {
  id: {
    type: Sequelize.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  status: DataTypes.STRING
})

Upvotes: 8

Related Questions