简体   繁体   中英

Sequelize Mysql - Unable to Insert model with one to many relationship

Design Overview: I've an application with Invoice creation and Inventory management features in it. Let's first understand the database design with 2 entities that we have as below:

  1. Invoices
  2. Items

Now, here I've a M:N relationship between these 2 entities because one invoice can contain multiple items and one item can be included in many such invoices.

So, I've created a 3rd table which we call joining table to associate these entities as shown in the image below,

数据库设计img

Problem Statemet: I'm unable to insert model in the child table(invoice_items) using include attribute. Look at the code below to understand what's wrong happening here?

3 Model Classes as below:

1. Invoice:

Note: Providing with fewer attributes to keep it short.

 module.exports = (sequelize, DataTypes) => { const Invoice = sequelize.define('Invoice', { invoiceId: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false, autoIncrement: true, primaryKey: true }, invoiceNumber: { type: DataTypes.INTEGER(6).UNSIGNED.ZEROFILL, allowNull: false, unique: true }, invoiceTotal: { type: DataTypes.DECIMAL(9,2), allowNull: false, defaultValue: 0.00 }, paymentTotal: { type: DataTypes.DECIMAL(9,2), allowNull: false, defaultValue: 0.00 }, invoiceDate: { type: DataTypes.DATEONLY, defaultValue: DataTypes.NOW, allowNull: false } }, { underscored: true }); Invoice.associate = function (model) { Invoice.belongsTo(model.Customer, { as: 'customer', foreignKey: { name: "cust_id", allowNull: false } }); // association with 3rd table Invoice.hasMany(model.InvoiceItem, { as: 'invoice_item', constraints: true, onDelete: 'NO ACTION', foreignKey: { name: "invoice_id", allowNull: false } }); }; return Invoice; }

2. Item:

Note: Providing with fewer attributes to keep it short.

 module.exports = (sequelize, DataTypes) => { const Item = sequelize.define('Item', { itemId: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false, autoIncrement: true, primaryKey: true }, itemName: { type: DataTypes.TEXT, allowNull: false, defaultValue: '' }, // this is a opening stock quantityInStock: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false, defaultValue: 0, }, unitPrice: { type: DataTypes.DECIMAL(9,2), allowNull: false, defaultValue: 0.00 } }, { underscored: true }); Item.associate = function (model) { // association with 3rd table Item.hasMany(model.InvoiceItem, { as: 'invoice_item', // alias name of a model constraints: true, onDelete: 'NO ACTION', foreignKey: { name: "item_id", allowNull: false } }); }; return Item; }

3. Invoice_Item:

Note: Providing with fewer attributes to keep it short.

 module.exports = (sequelize, DataTypes) => { const InvoiceItem = sequelize.define('InvoiceItem', { invoiceItemId: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false, autoIncrement: true, primaryKey: true }, quantity: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false, defaultValue: 0, }, rate: { type: DataTypes.DECIMAL(9,2), allowNull: false, defaultValue: 0.00 } }, { underscored: true }); InvoiceItem.associate = function(model) { InvoiceItem.belongsTo(model.Invoice, { as: 'invoice', foreignKey: { name: "invoice_id", allowNull: false } }); InvoiceItem.belongsTo(model.Item, { as: 'item', foreignKey: { name: "item_id", allowNull: false } }); } return InvoiceItem; }

Now, I'm using below code to create an invoice with the list of items in it. But, this is not inserting the child records in the joining table( invoice_items ). What's wrong here in the code below?

 invoice = await Invoice.create({ "invoiceNumber": req.body.invoiceNumber, "invoiceDate": req.body.invoiceDate, "invoiceTotal": req.body.invoiceTotal, "paymentTotal": req.body.paymentTotal, "cust_id": req.body.customer.custId, invoice_items: [{ item_id: 1, quantity: 2, rate: 300 }] }, { include: [{ association: InvoiceItem, as: 'invoice_item' }] });

After trying so many variations, I understand that there was a problem in the association of my model classes. And, below is the way of associating both Invoice and Item model classes for M:N(Many to Many) relationships. I can now update the join table(invoice_items) by inserting the record in it for each invoice we create in the system with the items in it.

 Invoice.associate = function (model) { // association in Invoice model class Invoice.belongsToMany(model.Item, { through: 'InvoiceItem', constraints: true, onDelete: 'NO ACTION', foreignKey: { name: "invoice_id", // foreign key column name in a table invoice_items table allowNull: false } }); };

 Item.associate = function (model) { // association in Item model class Item.belongsToMany(model.Invoice, { through: 'InvoiceItem', constraints: true, onDelete: 'NO ACTION', foreignKey: { name: "item_id", // foreign key column name in a table invoice_items allowNull: false } }); };

Create Invoice with Items in it:

Note: Passing itemId (1) as a parameter in the addItems() method. If you have multiple items in an invoice then you can add forEach loop here to iterate over each item and individually pass the itemId , quantity and rate for an item sold to the customer.

 // first create the invoice invoice = await Invoice.create(invoice); // Next, add record in the join table await invoice.addItems([1], { through: { quantity: item.quantity, rate: item.rate } });

Database Tables with one Test Result:

1. Invoice Table:

发票表

2. Invoice_items Table(Join Table):

发票项目表

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM