My Product model:
const { Model, DataTypes } = require('sequelize');
class Product extends Model {
static init(connection) {
super.init({
name: DataTypes.STRING,
description: DataTypes.TEXT,
purchase_price: DataTypes.DOUBLE,
sale_price: DataTypes.DOUBLE
}, {sequelize: connection});
}
static associate(models) {
this.belongsToMany(models.Purchase, {
foreignKey: 'product_id', through: 'products_purchases', as: 'purchases'
});
}
}
module.exports = Product;
My Purchase Model:
const { Model, DataTypes } = require('sequelize');
const Product = require('../models/Product');
class Purchase extends Model {
static init(connection) {
super.init({
request_number: DataTypes.STRING,
datetime: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW
},
email: DataTypes.STRING,
status: DataTypes.ENUM([
'orderPlaced', 'paymentMade','paymentConfirmed', 'forwardedProduct','productReceived','orderCanceled'
])
}, {sequelize: connection});
}
static associate(models) {
this.belongsTo(models.User, {foreignKey: 'user_id', as: 'user'});
this.belongsToMany(models.Product, {
foreignKey: 'purchase_id', through: 'products_purchases', as: 'products'
});
}
}
module.exports = Purchase;
And finally my ProductPurchase Model:
(Note that there is some extra columns in the relationship table)
const { Model, DataTypes } = require('sequelize');
class ProductPurchase extends Model {
static init(connection) {
super.init({
product_value: DataTypes.FLOAT,
product_quantity: DataTypes.INTEGER
}, {
sequelize: connection,
modelName: 'products_purchases'
});
}
}
module.exports = ProductPurchase;
I want to insert data into purchases table and also into products_purchases. I tried to do it the way below, but I don't know how to send the data of the extra columns from ProductPurchase:
async store(req, res) {
try {
const { user_id } = req.params;
const user = await User.findByPk(user_id);
if (!user) {
return res.status(400).json('User not found!');
}
// productsData = [{ productId, productPrice, quantity }];
const { request_number, datetime, email, status, productsData } = req.body;
const purchase = await Purchase.create({
request_number, datetime, email, status, user_id
});
const productsIds = productsData.map(product => {
return product.productId;
});
const products = await Product.findAll({
where: {id: productsIds}
});
await purchase.addProduct(products);
return res.json(products);
} catch (error) {
return res.status(500).json(error);
}
}
Can anybody help me?
Related
These are my models:
Staff
const { DataTypes } = require('sequelize');
const sequelize = require('../sequelize')
const Staff = sequelize.define('Staff', {
name: {
type: DataTypes.STRING,
allowNull: false,
},
SSN: {
type: DataTypes.INTEGER,
},
email:{
type: DataTypes.STRING,
}
}, {
timestamps: true
});
Staff.associate = function (models) {
Staff.belongsToMany(models.Technology, {through: models.StaffTechnology});
};
module.exports = Staff;
Technology:
const { DataTypes } = require('sequelize');
const sequelize = require('../sequelize')
const Technology = sequelize.define('Technology', {
name: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.STRING,
}
}, {
timestamps: true
});
Technology.associate = function (models) {
Technology.belongsToMany(models.Staff, { through: models.StaffTechnology});
};
module.exports = Technology;
This will be the join table:
const { DataTypes } = require('sequelize');
const sequelize = require('../sequelize')
const Staff = require('../../database/models/staff.model');
const Technology = require('../../database/models/technology.model');
const StaffTechnology = sequelize.define('StaffTechnology', {
experience: {
type: DataTypes.INTEGER,
},
StaffID: {
type: DataTypes.INTEGER,
references: {
model: Staff,
key: 'id'
}
},
TechnologyID: {
type: DataTypes.INTEGER,
references: {
model: Technology,
key: 'id'
}
},
}, {
timestamps: true
});
StaffTechnology.associate = function (models) {
//StaffTechnology.hasMany(models.Staff, { foreignKey: 'StaffID' });
//StaffTechnology.hasMany(models.Technology, { foreignKey: 'TechnologyID' });
};
module.exports = StaffTechnology
Right now I cant do a Staff.findAll({include:Technology}), since it give me an error saying that Staff and Technology are not associated, but I saw Sequelize documentation, and I saw a very similar example working.
What I want is the posibility to return All Staffs with their technologies, Staff can have many technologies, and Technologies can belong to Many Staffs, but in reality they are connected through StaffTechnology join table.
You need to declare the associations inside of your index.js (or where you initialize your DB). You can either do it manually by declaring the associations directly by requiring the models and doing
Model1.association(Model2, {
through: "JoinTable"
}
Model2.association(Model1, {
through: "JoinTable"
}
Or do it programmatically (If you are already using that approach to initialize your DB):
'use strict'
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const config = {
"username": "root",
"password": "YOUR ROOT PASSWORD HERE",
"database": "YOUR DATABASE NAME HERE",
"host": "127.0.0.1",
"dialect": "mysql"
}
const db = {}
let sequelize = new Sequelize(config.database, config.username, config.password, config);
fs.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0)
&& (file !== basename)
&& (file.slice(-3) === '.js');
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
^ This code does both the initialization and the associations in one. You need to make sure that your model files and the initialization file are in the same folder.
I wrote a class which extends a Model, and I need create a method to compareSync password:
const { Model, DataTypes } = require('sequelize');
class User extends Model {
static init(sequelize) {
super.init({
username: DataTypes.STRING,
password: DataTypes.STRING,
role: DataTypes.STRING,
status: DataTypes.INTEGER
},
{
sequelize,
hooks: {
beforeCreate: (user) => {
const salt = bcrypt.genSaltSync();
user.password = bcrypt.hashSync(user.password, salt);
}
}
}
)
}
static associate(model) {
this.belongsToMany(models.Movie, { through: models.Ratings });
}
}
module.exports = User;
The hook is working, I thought to add after beforeCreate a:
instanceMethods: {
validPassword: function (password) {
return bcrypt.compareSync(password, this.password);
}
}
Using this class how I can define an user method?
I got this:
const { Model, DataTypes } = require('sequelize');
const bcrypt = require("bcrypt")
class User extends Model {
static init(sequelize) {
super.init({
username: DataTypes.STRING,
password: DataTypes.STRING,
role: DataTypes.STRING,
status: DataTypes.INTEGER
},
{
sequelize,
hooks: {
beforeCreate: (user) => {
const salt = bcrypt.genSaltSync();
user.password = bcrypt.hashSync(user.password, salt);
}
}
}
)
}
static associate(model) {
this.belongsToMany(models.Movie, { through: models.Ratings });
}
validPassword(password) {
return bcrypt.compareSync(password, this.password);
}
}
module.exports = User;
If I wanna use the method inside controller for example:
const login = async (req, res) => {
const { username, password } = req.params;
const user = await User.findOne({
where: { username }
});
if (!user) {
return res.status(400).send("User not find!")
}
if(!user.validPassword(password)){..}
res.send(user)
}
I'm trying to build a relation between the users and his favorite cryptocurrency.
Meaning, multiple Users can have the same crypto as their favorites.
Coin.js
module.exports = (sequelize, DataTypes) => {
const Coin = sequelize.define('Coin', {
cryptoName: DataTypes.STRING,
})
Coin.associate = function(models) {
Coin.belongsToMany(models.CoinsUserRelations, {foreignKey: 'CoinID', through: 'CoinsUserRelations'})
}
return Coin
}
User.js
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
email: {
type: DataTypes.STRING,
unique: true
},
password: DataTypes.STRING
}, {
hooks: {
beforeSave: hashPassword
}
})
User.associate = function(models) {
User.belongsToMany(models.CoinsUserRelations, {foreignKey: 'UserID', through: 'CoinsUserRelations'})
}
return User
}
These are my 2 tables at the moment and now I'm trying to build a relation between them. and I keep receiving this error on my console which I do not understand why.
I've edited multiple time to resolve the issue, This was the result with the help of Anatoly
CoinsUserRelations.js
module.exports = (sequelize, DataTypes) => {
const CoinsUsersRelations = sequelize.define('CoinsUsersRelations', {
UserID: {
type: DataTypes.INTEGER,
},
CoinID: {
type: DataTypes.INTEGER,
}
})
return CoinsUsersRelations
}
I'm really unsure on why I get this error if I'm actually defining the model before creating the relations..
Index.js
const fs = require('fs')
const path = require('path')
const Sequelize = require('sequelize')
const config = require('../config/config')
const db = {}
const sequelize = new Sequelize(
config.db.database,
config.db.user,
config.db.password,
config.db.options
)
fs
.readdirSync(__dirname)
.filter((file) =>
file !== 'index.js'
)
.forEach((file) => {
const model = sequelize.import(path.join(__dirname, file))
db[model.name] = model
})
Object.keys(db).forEach(function (modelName) {
if (db[modelName].associate) {
db[modelName].associate(db)
}
})
db.sequelize = sequelize
db.Sequelize = Sequelize
module.exports = db
Edit part for a bug called SequelizeEagerLoadingError
module.exports = {
async getUserFavoriteCrypto (req, res) {
try {
const coins = await db.Users.findOne({
where: {
UserID : req.query.userId
},
attributes: ['UserID'],
include: [{
model: db.Coins,
required: false
}]
})
console.log(coins)
res.send({
coins: coins
})
} catch (err) {
res.status(400).send({
error: err
})
}
}
}
You should register all models and only after that all their associations in one place. See my answer on a similar problem
I'm trying to use a sequelize N:M relation trough another table but I keep getting this error:
throw new Error(${this.name}.belongsToMany called with something that's not a subclass of Sequelize.Model);
Error: Cases.belongsToMany called with something that's not a subclass of Sequelize.Model
Here are my classes:
models/User.js
const Sequelize = require('sequelize');
const sql = require('../config/sql');
const DocumentType = require('./DocumentType');
const Case = require('./Case');
const User = sql.define('Users', {
firstName: Sequelize.STRING,
lastName: Sequelize.STRING,
email: Sequelize.STRING,
document: Sequelize.INTEGER,
documentTypeId: Sequelize.INTEGER,
password: Sequelize.STRING,
active: Sequelize.INTEGER
});
User.belongsTo(DocumentType, {foreignKey: 'documentTypeId' ,as: 'documentType'});
User.belongsToMany(Case, { through: 'CaseUser' });
User.findById = (id) => {
return User.findOne({
where: {
id: id
},
include: 'documentType'
});
};
User.findByEmail = (email) => {
return User.findOne({
where: {
email: email
},
include: 'documentType'
});
};
User.checkActiveStatus = (id) => {
return User.findOne({
where: {
id: id
},
attributes: ['active']
});
};
module.exports = User;
models/Case.js
const Sequelize = require('sequelize');
const sql = require('../config/sql');
const User = require('./User');
const Case = sql.define('Cases', {
description: Sequelize.STRING,
startDate: Sequelize.STRING
});
Case.belongsToMany(User, {through: 'CaseUser'});
module.exports = Case;
I get the error only in the Case.belongsToMany(User, { through: 'CaseUser' }); and not in the User.belongsToMany(Case, { through: 'CaseUser' }); one
The weird thing is that if I switch the belongsToMany(User... for belongsToMany(DocumentType... and import the DocumentType file I don't get the error. The DocumentType Model is virtually identical to the User one
models/DocumentType.js
const Sequelize = require('sequelize');
const sql = require('../config/sql');
const DocumentType = sql.define('DocumentTypes', {
name: Sequelize.STRING,
slug: Sequelize.STRING
}, {
timestamps: false
});
module.exports = DocumentType;
Any ideas on what could be causing this behavior?
Thanks!
You should try to avoid circular dependencies.
Remove lines
const Case = require('./Case');
and
Case.belongsToMany(User, {through: 'CaseUser'});
from User.js
Remove lines
const User = require('./User');
and
User.belongsToMany(Case, { through: 'CaseUser' });
from Case.js
Create associations.js
const Case = require('./Case');
const User = require('./User');
Case.belongsToMany(User, {through: 'CaseUser'});
User.belongsToMany(Case, { through: 'CaseUser' });
Here's my query that's failing:
Models.OrdersProducts.create({
orderId: 1,
productId: 1,
});
with the error:
Executing (default): INSERT INTO "OrdersProducts" ("orderId","productId","createdAt","updatedAt","OrderId") VALUES (1,1,'2018-02-25 12:51:00.110 +00:00
','2018-02-25 12:51:00.110 +00:00',NULL) RETURNING *;
Unhandled rejection SequelizeDatabaseError: column "OrderId" of relation "OrdersProducts" does not exist
at Query.formatError (/Users/aakashverma/Documents/sequelize-join-table/node_modules/sequelize/lib/dialects/postgres/query.js:363:16)
where my table doesn't have the column OrderId(with capital O) and only orderId
NOTE: My Orders and Products tables do have an entry with id 1, that's not the problem.
This is my migration for OrdersProducts:
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('OrdersProducts', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
orderId: {
type: Sequelize.INTEGER,
references: {
model: 'Orders',
key: 'id',
},
},
productId: {
type: Sequelize.INTEGER,
references: {
model: 'Products',
key: 'id',
},
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
}),
down: (queryInterface, Sequelize) => queryInterface.dropTable('OrdersProducts'),
};
and here are the models file:
models/OrdersProducts.js
module.exports = (sequelize, DataTypes) => {
const OrdersProducts = sequelize.define('OrdersProducts', {
orderId: DataTypes.INTEGER,
productId: DataTypes.INTEGER,
}, {});
return OrdersProducts;
};
models/Orders.js
module.exports = (sequelize, DataTypes) => {
const Orders = sequelize.define('Orders', {
userId: DataTypes.INTEGER,
}, {});
Orders.associate = function (models) {
Orders.belongsTo(models.Users);
Orders.belongsToMany(models.Products, { through: 'OrdersProducts', as: 'product' });
};
return Orders;
};
models/Products.js
module.exports = (sequelize, DataTypes) => {
const Products = sequelize.define('Products', {
name: DataTypes.TEXT,
}, {});
Products.associate = function (models) {
Products.belongsToMany(models.Orders, { through: 'OrdersProducts', as: 'order' });
};
return Products;
};
Weird.
N.B. I am using 1 as the id in queries because this is the autoincrement id key value as I only add one entry in any table whatsoever.
Looking at my Orders model:
module.exports = (sequelize, DataTypes) => {
const Orders = sequelize.define('Orders', {
userId: DataTypes.INTEGER,
}, {});
Orders.associate = function (models) {
Orders.belongsTo(models.Users);
Orders.belongsToMany(models.Products, { through: 'OrdersProducts', as: 'product' });
};
return Orders;
};
and myProducts model:
module.exports = (sequelize, DataTypes) => {
const Products = sequelize.define('Products', {
name: DataTypes.TEXT,
}, {});
Products.associate = function (models) {
Products.belongsToMany(models.Orders, { through: 'OrdersProducts', as: 'order' });
};
return Products;
};
These two seem to create attributes of their own for my OrdersProducts table as OrderId and ProductId (with first letter capital; don't know why and how they are generating the name) and therefore, I was getting that OrderId thingy.
If I remove the belongToMany association from the Orders model, I stared to get the same error with ProductId column instead.
Finally, I found the accepted answer here which told me, in a way, to use the columns provided by the associations and drop the corresponding in the OrdersProducts column.
Now my query, which works fine BTW, looks like:
const Models = require('./models');
// Models.Users.create({
// name: 'Aakash',
// });
// Models.Orders.create({
// userId: 1,
// });
// Models.Products.create({
// name: 'lash',
// });
Models.OrdersProducts.create({
OrderId: 1,
ProductId: 1,
});
and model looks like (pay attention to the capital letters, again):
module.exports = (sequelize, DataTypes) => {
const OrdersProducts = sequelize.define('OrdersProducts', {
OrderId: DataTypes.INTEGER,
ProductId: DataTypes.INTEGER,
}, {});
return OrdersProducts;
};
The mystery about the automatic column naming still exists.
P.S. This is the weirdest part.
Now I have a table Users as you'd have been able to see from my code above. Pay attention to how the belongsTo plays in the models.
models/users.js
module.exports = (sequelize, DataTypes) => {
const Users = sequelize.define('Users', {
name: DataTypes.STRING,
}, {});
Users.associate = function (models) {
Users.hasMany(models.Orders);
};
return Users;
};
models/orders.js
module.exports = (sequelize, DataTypes) => {
const Orders = sequelize.define('Orders', {
userId: DataTypes.INTEGER,
}, {});
Orders.associate = function (models) {
Orders.belongsTo(models.Users);
Orders.belongsToMany(models.Products, { through: 'OrdersProducts', as: 'product' });
};
return Orders;
};
The query below still works and adds an entry to Orders (look at how it doesn't require me to remove userId column from the Orders table nor does it come up with its own UserId -_-):
Models.Users.create({
name: 'Aakash',
});
Models.Orders.create({
userId: 1,
});
models/users.js
module.exports = (sequelize, DataTypes) => {
const Users = sequelize.define('Users', {
name: DataTypes.STRING,
}, {});
Users.associate = function (models) {
Users.hasMany(models.Orders);
};
return Users;
};
models/orders.js
module.exports = (sequelize, DataTypes) => {
const Orders = sequelize.define('Orders', {
userId: DataTypes.INTEGER,
}, {});
Orders.associate = function (models) {
Orders.belongsTo(models.Users);
Orders.belongsToMany(models.Products, { through: 'OrdersProducts', as: 'product' });
};
return Orders;
};