Sequelize include options cannot get another models - mysql

I have two model that are belongs to each other (order_items.js & products.js) productId as a foreign key in order_items, code as below:
order_items.js
const { DataTypes } = require('sequelize')
const db_config = require(`../config/config`)
const Product = require('./product')
const OrderItem = db_config.define('order_item', {
productId : { type: DataTypes.INTEGER, allowNull:false, references: {model: Product, key: 'id'} },
quantity: { type: DataTypes.INTEGER }
}, {
freezeTableName: true
})
module.exports = OrderItem
product.js
const { DataTypes } = require('sequelize')
const db_config = require(`../config/config`)
const Category = require('./category')
const Product = db_config.define('product', {
productName : { type: DataTypes.STRING, allowNull:false },
productPrice: { type: DataTypes.INTEGER, allowNull:false },
productDescription: { type: DataTypes.STRING, allowNull:true },
productImage: { type: DataTypes.STRING, allowNull:true },
productStock: { type: DataTypes.INTEGER, validate: { min: 0 }, defaultValue: 0, allowNull: false },
CategoryId: { type: DataTypes.INTEGER, allowNull:false, defaultValue: 1, references: {model: Category, key: 'id'}}
}, {
freezeTableName: true
})
module.exports = Product
order_routes.js
router.get('/', async (req, res) => {
try {
const dataList = await OrderItem.findAll({include: [{model:Product, required:true}]})
res.send({
status: "success",
message: "data found",
data: dataList
})
} catch (err) {
res.send({
status: "failed",
message: err})
}
})
Result in postman
Can anyone help please? what I'm trying to do is that when I get the order_item, it also get the products refers to the id of the products

Where are the associations in the model definition? I see a reference on column field but you also needs to do below definitions seperately
Inside OrderItem Model File
OrderItem.associate = models => {
OrderItem.belongsTo(Product, {as: "product", foreignKey: "productId", sourceKey: "id"});
};
Inside Products Model File
Product.associate = models => {
Product.hasMany(OrderItem, {as: "orders", foreignKey: "productId", sourceKey: "id"});
};
Also I would suggest you to store price in OrderItem collection as well so in case in future when the product price changes your past order data is not incorrect.

Related

SequelizeEagerLoadingError : associated not working in sequelize

I have two tables , Parent table is checkout_product and it's child table is product_attributes
Now I am creating association between two tables so it's getting error:
Here is my code
const Sequelize = require('sequelize');
const { checkout_product_view } = require('../controller/checkoutProductController');
const sequelize = new Sequelize('celeb', 'root', '', {
host: 'localhost',
dialect: 'mysql' ,
});
var Checkout_product = sequelize.define('checkout_products',{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
name: {
type: Sequelize.STRING
},
price : {
type : Sequelize.STRING
},
image : {
type: Sequelize.STRING
},
quantity : {
type: Sequelize.STRING
},
});
Checkout_product.associate = function(models) {
checkout_products.hasMany(models.Product_attributes, {
foreignKey: 'product_id',
});
};
sequelize.sync()
.catch(error => console.log('This error occured', error));
module.exports = Checkout_product;
And here is my product_attributes model:
const Sequelize = require('sequelize');
const sequelize = new Sequelize('celeb', 'root', '', {
host: 'localhost',
dialect: 'mysql' ,
});
var Product_attribute = sequelize.define('product_attribute',{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
product_id: {
type: Sequelize.INTEGER
},
attribute_name : {
type : Sequelize.STRING
},
attribute : {
type : Sequelize.STRING
},
price : {
type : Sequelize.STRING
}
});
Product_attribute.associate = (models) => {
Product_attribute.belongsTo(models.Checkout_product, {
foreign_key: 'product_id',
sourceKey: 'id'
});
};
sequelize.sync()
.then(() => console.log('Product_attribute'))
.catch(error => console.log('This error occured', error));
module.exports = Product_attribute;
Product has many product_attributes so whenever I called following function getting this error :
module.exports.checkout_product_get = function (req, res) {
Checkout_product.findAll({
include: [ {
model : Product_attributes
}]
})
.then(checkout_product => {
console.log(checkout_product),
res.json(checkout_product)
})
}
Error : UnhandledPromiseRejectionWarning: SequelizeEagerLoadingError: product_attribute is not associated to checkout_products!
You didn't call associate methods for all registered models.
I recommend you to define and register all models and associations like I described in this answer

date_format with sequelize mysql

I am using sequelize for my backend and I want to add a where condition with YYYY-mm-dd
In mysql we use date_format(dateCreated, "%d-%m-%Y").
But how to achieve it in Sequelize. I searched all over the google but nothing helped me out
My present Sequelize query. I want to get data of dateCreated = '2020-05-31'.
const apartmentOrdersData = await apartments_order.findAll(
{
where: { apid: req.body.apid }, group: ['apcid'],
attributes: ['apcid', [sequelize.fn('sum', sequelize.col('totalCost')), 'total_amount'],],
include: [apartments_child]
});
My model:
const { DataTypes, Model } = require('sequelize');
const sequelize = require('../../../mysql_connection/sequilize');
const admins = require('./admins');
const apartments_child = require('./apartments_child');
class apartments_order extends Model { }
apartments_order.init({
// Model attributes are defined here
apoid: {
type: DataTypes.INTEGER,
primaryKey: true
},
invoiceNo: {
type: DataTypes.STRING
},
apid: {
type: DataTypes.INTEGER
},
apcid: {
type: DataTypes.INTEGER
},
apcbid: {
type: DataTypes.INTEGER
},
totalCost: {
type: DataTypes.DECIMAL
},
dateCreated: {
type: DataTypes.DATE
},
dateUpdated: {
type: DataTypes.DATE
},
createdBy: {
type: DataTypes.STRING
},
updatedBy: {
type: DataTypes.STRING
},
status: {
type: DataTypes.STRING
}
}, {
sequelize,
timestamps: false,
logging: false,
tableName: 'apartments_order'
});
apartments_order.hasOne(admins, { foreignKey: 'aid', sourceKey: 'createdBy' });
apartments_order.hasOne(apartments_child, { foreignKey: 'apcid', sourceKey: 'apcid' });
module.exports = apartments_order;
One thing that you can do is use a setter and getter for the columns.
Follow an example (on your field declaration, you can manipulate the data inside getter() and setter():
dateUpdated: {
type: Sequelize.STRING,
defaultValue: '[]',
get() {
const d = this.getDataValue('dateUpdated');
return format(d, 'Y-m-d');
},
set(value) {
return this.setDataValue('dateUpdated', format(value, <your-format-here>)
},
type: DataTypes.DATE,
},

Able to make migration with associations but not able to query with associations (association not found)

I've built a database with a couple tables that are associated with each other: Auction and Bids. Each Auction should have many Bids and each Bid should have only one Auction. I made a migration fine to add foreignKeys but when I try to look up a certain Bid on an Auction I receive a SequelizeEagerLoadingError: Bids is not associated to Auctions! error.
migrations file:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn(
'Auctions', // name of target model
'BidId',
{
type: Sequelize.INTEGER,
references:{
model: "bids",
key: "bid_id",
},
},
);
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Auction');
}
};
bids.js
const Sequelize = require('sequelize');
// const resolver = require('graphql-sequelize');
const sequelize = require('../config/database');
const Auction = require('./Auction');
const tableName = 'bids';
const Bids = sequelize.define('Bids', {
bid_id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
createdAt: {
type: Sequelize.DATE,
// defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
},
amount: {
type: Sequelize.INTEGER
},
bid_amount: {
type:Sequelize.STRING
},
bid_no: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
},
},
{tableName})
Bids.associate = () => {
Bids.hasOne(Auction, {foreignKey:"BidId"})
};
auction.js
const Sequelize = require('sequelize');
const sequelize = require('../config/database');
const tableName = 'Auctions';
const Auction = sequelize.define('Auctions', {
auc_id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4, // generate the UUID automatically
primaryKey: true,
},
features: {
type: Sequelize.JSONB,
},
bid_amount: {
type:Sequelize.STRING
},
BidId: {
type: Sequelize.UUID,
}
}, { tableName });
Auction.hasMany(Bids, {foreignKey: 'BidId'})
module.exports = Auction
query.js
const findBidOnAuction = () => {Auction.findOne({where:{BidId:2}, include:[{model:Bids}]}).then(data => console.log("result", data))}
How do I properly associate these tables?
edit: Also on pgAdmin I can that the relationship exists, BidId is a foreignKey on Auction linked to bid_id on Bids
I do the following for add a foreignKey on migrations:
1.I create a migration for the parent model with a ref function.
const TABLE_NAME = 'parent';
function up(queryInterface, Sequelize) {
return queryInterface.createTable(
TABLE_NAME, //Bid model
{
//attributes
},
);
}
function down(queryInterface, Sequelize) {
return queryInterface.dropTable(TABLE_NAME);
}
function ref(Sequelize) {
return {type: Sequelize.STRING(2), references: {model: TABLE_NAME, key: 'id'}}; // 'id' here is your parent (Bids) primary key
}
module.exports = {up, down, ref};
2.On the child model where you are going to add the reference, you import the ref function and added like this, so your migration actually knows which model are you refering:
const {ref: REF_Model} = require('name-of-your-migration-file'); //import model
async function up(queryInterface, Sequelize){
await queryInterface.addColumn('Auctions', 'BidId', {...REF_Model(Sequelize), allowNull: true});
}
async function down(queryInterface){
await queryInterface.removeColumn('Auctions', 'BidId');
}
module.exports = {up, down};

Unable to insert id into a table that belongs to a foreign key referenced table using Sequelize

I am building serverless application using node js and using claudia-api-builder as a framework to launch APIs in AWS.
In app.js file, i call the required api as
const ApiBuilder = require('claudia-api-builder');
const api = new ApiBuilder();
module.exports = api;
api.post('/api/auth/validatephonenumber', async function (request)
{
return new Promise((resolve, reject) => {
index.loadDatabase().then((db) => {
resolve(loginController.validatePhonenumber(db, request));
});
});
});
Below is my code:
async function validatePhonenumber(db, request) {
return new Promise(
async (resolve, reject) => {
let emailid;
await db.EmailRegistration.sync().then(function () {
emailid = db.EmailRegistration.findOne({
where: { email: { [Op.eq]: mailid } },
attributes: ['id'],
});
});
if (emailid != null) {
console.log(`email id: ${emailid.id}`);
await db.ContactDetails.sync().then(function () {
db.ContactDetails.findOrCreate({
where: { contactnumber: phnum },
defaults: { EmailRegistrationId: emailid.id },
}).spread((contactdetails, created) => {
console.log(`contactdetails: ${contactdetails}`);
if (contactdetails !== null) {
resolve({ statuscode: indexController.statusCode.statusOK, contactdetails: contactdetails })
} else {
reject({ statuscode: indexController.statusCode.InternalServerError, message: 'phone number not created' });
}
});
});
};
});
}
I am trying to add a emailregistrationid of EmailRegistration table into ContactDetails table as a foreign key reference. I am using sequelize with mysql, nodejs to achieve the desired results. But, i am getting below error:
Unhandled rejection SequelizeForeignKeyConstraintError: Cannot add or update a child row: a foreign key constraint fails (inmeeydb.ContactDetails, CONSTRAINT ContactDetails_ibfk_1 FOREIGN KEY (EmailRegistrationId) REFERENCES EmailRegistration (id) ON DELETE CASCADE ON UPDATE CASCADE)
Below is my EmailRegistration models file:
const moment = require('moment');
module.exports = (sequelize, DataTypes) => {
const EmailRegistration = sequelize.define(
'EmailRegistration',
{
id: {
type: DataTypes.UUID,
primaryKey: true,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
},
email: {
type: DataTypes.STRING(50),
allowNull: false,
},
password: {
type: DataTypes.STRING,
allowNull: false,
validate: { min: 6 },
},
createdAt: {
type: DataTypes.DATE,
get() {
return moment.utc(new Date(), 'DD/MM/YYYY h:mm a').format('DD/MM/YYYY h:mm a');
},
},
updatedAt: {
type: DataTypes.DATE,
defaultValue: null,
},
},
{
freezeTableName: true,
}
);
EmailRegistration.associate = function (models) {
EmailRegistration.hasOne(models.ContactDetails,
{ foreignKey: 'EmailRegistrationId' }
);
};
return EmailRegistration;
};
Below is my Contactdetails models file:
const moment = require('moment');
module.exports = (sequelize, DataTypes) => {
const ContactDetails = sequelize.define(
'ContactDetails',
{
id: {
type: DataTypes.UUID,
primaryKey: true,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
},
contactnumber: { type: DataTypes.STRING(13), allowNull: false },
isverified: { type: DataTypes.BOOLEAN, defaultValue: false },
createdAt: {
type: DataTypes.DATE,
get() {
return moment.utc(new Date(), 'DD/MM/YYYY h:mm a').format('DD/MM/YYYY h:mm a');
},
},
updatedAt: {
type: DataTypes.DATE,
defaultValue: null,
},
},
{
indexes: [{ fields: ['contactnumber'], unique: true }],
},
{
freezeTableName: true,
}
);
ContactDetails.associate = function(models) {
ContactDetails.belongsTo(models.EmailRegistration, {
onDelete: 'CASCADE',
hooks: true,
foreignKey: { allowNull: false },
});
};
return ContactDetails;
};
I tried to change the code as below with reference in both tables, but nothing worked.
ContactDetails.associate = function(models) {
ContactDetails.belongsTo(models.EmailRegistration,
{ foreignKey: 'EmailRegistrationId' }
);
};
Not able to analyze how to overcome the issue. This worked fine when i used nodejs with expressjs and had no issues. It fails to identify the EmailRegistrationId(that is missing in the query) in ContactDetails table and shows the output as
INSERT INTO `ContactDetails` (`id`,`contactnumber`,`isverified`,`createdAt`,`updatedAt`) VALUES ('52974e07-8489-4101-ab71-6af874903290','+xxxxxxxxx',false,'2018-10-12 08:55:35','2018-10-12 08:55:35');
You need to update the configuration of your association. The ContactDetails model will now have a field called emailregistrationid
EmailRegistration.associate = function (models) {
EmailRegistration.hasMany(models.ContactDetails);
};
ContactDetails.associate = function(models) {
ContactDetails.belongsTo(models.EmailRegistration, {
onDelete: 'CASCADE',
hooks: true,
foreignKey: {
name: 'emailregistrationid'
allowNull: false
},
});
}
ContactDetails.create({
...
emailregistrationid: <some_valid_emailregistrationid>
})

How use JOIN LEFT in Sequelize?

I have two models tables, like
Project = Sequelize.define('Project',{
name: Sequelize.STRING
});
Task = Sequelize.define('Task',{
name:Sequelize.STRING,
status: Sequelize.BOOLEAN,
deadline: Sequelize.DATE,
from_name: Sequelize.STRING,
ProjectId: Sequelize.INTEGER
});
I need to find all records from two tables. How do I perform this query in Sequelize?
SELECT * FROM mydb.Projects
LEFT JOIN mydb.Tasks ON Projects.id = Tasks.Project_id
I using:
exports.list = function (req, res) {
Project.hasMany(Task,{ foreignKey: { allowNull: false }, onDelete: 'CASCADE' });
Project.all({
include: [{
model: Task,
required:false,
where:{ProjectId: Sequelize.col('Project.id')}
}]
})
.then(function (projects) {
res.render('TODOList', {title: 'TODO', projects: projects || [] });
console.log(projects);
})
.catch(function (err) {
if (err) {
res.render('TODOList', {title: 'TODO List'});
}
});
};
but I get records only from Project, like:
Project {
dataValues: {
id: 5,
name: 'pr2',
createdAt: 2017-08-20T07:03:09.000Z,
updatedAt: 2017-08-20T07:41:47.000Z,
Tasks: [Object]
}
}
How I can get records Task (Project.dataValues.Task)?
So models are (say it: db/models.js):
const
Project = Sequelize.define(
'Project',
{
name: Sequelize.STRING
}),
Task = Sequelize.define(
'Task',
{
name: Sequelize.STRING,
status: Sequelize.BOOLEAN,
deadline: Sequelize.DATE,
from_name: Sequelize.STRING,
ProjectId: Sequelize.INTEGER
});
// relations must be defined once in model definition part (not like You do in every request
Project.hasMany(Task, {foreignKey: {allowNull: false}, onDelete: 'CASCADE'});
Task.belongsTo(Project);
module.exports = {Project, Task};
And in Your router's handler (say it routes/projects/list.js):
const Sequelize = require('sequelize');
const {Project, Task} = require('../../db/models');
module.exports.list = (req, res) => {
const query = {
include: [{
model: Task,
as: 'tasks',
required: false
}]
};
Project
.all(query)
.then(function (projects) {
// it's not recommended, but You insist to get read of model instances and have normal array of objects
projects = projects ? projects.map(project => project.toJSON()) : [];
res.render(
'TODOList',
{
title: 'TODO',
projects
});
console.log(projects);
})
.catch(function (err) {
console.error(err);
res.render('TODOList', {title: 'TODO List'});
});
};
but since sequelize returns array of sequelize model instances - it's normal to have dataValues don't worry just use as usual:
for(let project of projects) {
console.log(project.tasks[0].name);
}