sequelize associations do not work, how to associate properly many to many and one to many?

429 Views Asked by At

I have a products table and each product has some materials it is made of, the materials are referenced from Materials table, I have only their ids in the product record.

My problem is that that I can't do the association properly: I got an error saying the following:

throw new AssociationError(${source.name}.belongsToMany(${target.name}) requires through option, pass either a string or a model); AssociationError [SequelizeAssociationError]: product.belongsToMany(material) requires through option, pass either a string or a model

when I pass this code models.material I know it is a string because I printed it on the terminal, so why am I getting this error? How can I make proper associations?

here the whole code:

 class Product extends Model {
    static associate(models) {
      Product.belongsToMany(models.material, {
        foreignKey: 'id',
        sourceKey: 'materials',
        constraints: false,
      });

      Product.belongsTo(models.user, {
        foreignKey: 'id',
        sourceKey: 'creator',
      });
    }
  }
  Product.init(
    {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
      },
      name: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      quantity: {
        type: DataTypes.DECIMAL,
        allowNull: false,
        defaultValue: 0.0,
      },
      materials: {
        type: DataTypes.ARRAY(DataTypes.UUID),
      },
      creator: {
        type: DataTypes.UUID,
      },
      updater: {
        type: DataTypes.UUID,
      },
    },
    {
      sequelize,
      modelName: 'product',
    }
  );
2

There are 2 best solutions below

2
Edward On

When making many to many relationships, way to go is to create some throughput table where both Product and Material model will be connected. We can call it inventory and inventory will have both productId and materialId. As for relations, product will have many inventories and material will also have many inventories. Inventory would look like this:


   module.exports = (sequelize, DataTypes) => {     
    class Inventory extends Model {         
    static associate(models) {
              this.Product = this.belongsTo(models.Product, {foreignKey: 
               'productId'});
              this.Material = this.belongsTo(models.Material , {foreignKey: 
               'materialId'});      }     }     
    Inventory.init({            
    id: { // you can just keep id of inventory too
                    allowNull: false,
                    autoIncrement: true,
                    primaryKey: true,
                    type: DataTypes.INTEGER,},
        productId: { allowNull: false, type: DataTypes.STRING},  
    materialId: { allowNull: false, type: DataTypes.STRING}
                        },      {           sequelize,          modelName: 'Inventory',             createdAt: 'created_at',            updatedAt: 'updated_at',});
        
        return Inventory; }

And then is products and materials model you would have something like this

// in product table
this.Inventory= this.hasMany(models.Inventory, {foreignKey: 'productId'});
// in inventory table
this.Inventory= this.hasMany(models.Inventory, {foreignKey: 'materialId'});

This should work and hope it all makes sense

0
FaFa On

I solved it in a similar way @Edward

  class Product extends Model {
    static associate(models) {
      const materials_list = sequelize.define('materials_list', {
        materialId: {
          type: DataTypes.UUID,
          references: {
            model: models.material,
            key: 'id',
          },
        },
        productId: {
          type: DataTypes.UUID,
          references: {
            model: models.product,
            key: 'id',
          },
        },
      });

      Product.belongsToMany(models.material, {
        through: materials_list,
      });

      Product.belongsTo(models.user, {
        foreignKey: 'creator',
        targetKey: 'id',
      });

      Product.belongsTo(models.user, {
        foreignKey: 'updater',
        targetKey: 'id',
      });
    }
  }
  Product.init(
    {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
      },
      name: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      quantity: {
        type: DataTypes.DECIMAL,
        allowNull: false,
        defaultValue: 0.0,
      },
      material_list: {
        type: DataTypes.ARRAY(DataTypes.UUID),
      },
      creator: {
        type: DataTypes.UUID,
      },
      updater: {
        type: DataTypes.UUID,
      },
    },
    {
      sequelize,
      modelName: 'product',
    }
  );
  return Product;
};