Sequelize - How to define ON DELETE action during model definition - node.js

I would like to ask you if it is possible to set ON DELETE action while defining a Sequelize model, for example:
sequelize.define('oAuthAccessTokens', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE,
accessToken: { type: Sequelize.STRING(256), allowNull: false },
expires: { type: Sequelize.DATE, allowNull: false },
scope: Sequelize.STRING(255),
clientId: {
type: Sequelize.STRING(80),
allowNull: false,
references: {
model: 'oAuthClients',
key: "clientId",
**onDelete: "cascade"** // here is my try
}
},
userId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'oAuthUsers',
key: "id",
**onDelete: "cascade"** // here is my try
}
}
});
I know it's possible to pass a proper option to hasOne() method, but what about such a model definition? I hope there is a way which is not mentioned in the official documentation.
Updated
The problem is solved now. The onDelete attribute should be outside the references one.
sequelize.define('oAuthAccessTokens', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE,
accessToken: { type: Sequelize.STRING(256), allowNull: false },
expires: { type: Sequelize.DATE, allowNull: false },
scope: Sequelize.STRING(255),
clientId: {
type: Sequelize.STRING(80),
allowNull: false,
onDelete: "cascade",
references: {
model: 'oAuthClients',
key: "clientId",
}
},
userId: {
type: Sequelize.INTEGER,
onDelete: "cascade",
allowNull: false,
references: {
model: 'oAuthUsers',
key: "id"
}
}
});

The problem is solved now. The onDelete attribute should be outside the references one.
sequelize.define('oAuthAccessTokens', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE,
accessToken: { type: Sequelize.STRING(256), allowNull: false },
expires: { type: Sequelize.DATE, allowNull: false },
scope: Sequelize.STRING(255),
clientId: {
type: Sequelize.STRING(80),
allowNull: false,
onDelete: "cascade",
references: {
model: 'oAuthClients',
key: "clientId",
}
},
userId: {
type: Sequelize.INTEGER,
onDelete: "cascade",
allowNull: false,
references: {
model: 'oAuthUsers',
key: "id"
}
}
});

Related

Sequelize hasMany assocaition

I am considering these 2 tables "exam_response" and "answer" for hasMany association.
Where both the tables contains "question_id". Using question_id I need the answers.
exam_response table
module.exports = (sequelize, DataTypes) => {
const exam_response = sequelize.define('exam_response', {
id: {
allowNull: false,
primaryKey: true,
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4
},
session_id: {
type: DataTypes.UUID,
allowNull: false
},
exam_id: {
type: DataTypes.UUID,
allowNull: false
},
user_id: {
type: DataTypes.UUID,
allowNull: false
},
question_id: {
type: DataTypes.UUID,
allowNull: false
},
answer_ids: {
type: DataTypes.ARRAY(DataTypes.UUID),
allowNull: false
},
is_correct: {
type: DataTypes.BOOLEAN,
allowNull: false
},
is_bookmarked: {
type: DataTypes.BOOLEAN,
allowNull: false
},
is_attempted: {
type: DataTypes.BOOLEAN,
allowNull: false
},
createdAt: {
type: DataTypes.DATE,
field: 'created_at'
},
updatedAt: {
type: DataTypes.DATE,
field: 'updated_at'
}
}, {});
exam_response.associate = function (models) {
// associations can be defined here
exam_response.hasMany(models.answer, {
foreignKey: 'question_id', sourceKey: 'question_id',as:'exam_answers'
});
};
answer table
'use strict';
module.exports = (sequelize, DataTypes) => {
const answer = sequelize.define('answer', {
//{
// "id":"",
// "question_id":"123",
// "position":0,
// "answer":"This is answer 1."
// }
id: {
allowNull: false,
primaryKey: true,
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4
},
question_id: {
allowNull: false,
type: DataTypes.UUID
},
position: {
allowNull: false,
type: DataTypes.INTEGER
},
answer: {
allowNull: false,
type: DataTypes.TEXT
},
publish_status: {
allowNull: false,
type: DataTypes.ENUM('published', 'unpublished', 'deleted')
},
language: {
type: DataTypes.ENUM('en', 'kn', 'hi')
},
createdAt: {
type: DataTypes.DATE,
field: 'created_at'
},
updatedAt: {
type: DataTypes.DATE,
field: 'updated_at'
}
}, {});
answer.associate = models => {
answer.belongsTo(models.question,{foreignKey:'question_id',as:'answers'});
answer.belongsTo(models.exam_response,{foreignKey:'question_id', targetKey: 'question_id',as:'exam_answers'});
};
return answer;
};
Query::
ExamResponse.findAll({
where: {
exam_id
},
include: [
{
model: Answer,as:'exam_answers'
}
],
}).then(resp => {
response.successGet(res, resp, 'Exam Response');
}).catch(next)
I am getting the output but associated part("exam_answers") is empty.
If I use raw query, i am able to get the output. But the Query is only fetching me the exam_response not the answer even though the value exists.

sequelize - inner join gives an error after added a foreign key

i have these 2 models:
module.exports = function(sequelize, DataTypes) {
return sequelize.define('services_prices', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true
},
service_id: {
type: DataTypes.INTEGER(11),
allowNull: true,
references: {
model: 'services',
key: 'id'
}
},
created_at: {
type: DataTypes.DATE,
allowNull: false
},
limit: {
type: DataTypes.INTEGER(11),
allowNull: true
},
price: {
type: DataTypes.INTEGER(11),
allowNull: true
}
});
};
which is parent of this model: (services_user_prices can override services_prices )
module.exports = function(sequelize, DataTypes) {
return sequelize.define('services_user_prices', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
user_id: {
type: DataTypes.INTEGER(11),
allowNull: true
},
created_at: {
type: DataTypes.DATE,
allowNull: false
},
currency: {
type: DataTypes.STRING(255),
allowNull: true
},
is_active: {
type: DataTypes.INTEGER(1),
allowNull: true,
defaultValue: '0'
},
is_trial: {
type: DataTypes.INTEGER(1),
allowNull: true,
defaultValue: '0'
},
start_date: {
type: DataTypes.DATE,
allowNull: false
},
end_date: {
type: DataTypes.DATE,
allowNull: true
},
price: {
type: DataTypes.INTEGER(11),
allowNull: true
},
bundle_price_id: {
type: DataTypes.INTEGER(11),
allowNull: true,
references: {
model: 'services_prices',
key: 'id'
}
}
});
};
when trying to join them i get an error:
EagerLoadingError: services_prices is not associated to services_user_prices!
const result= await db.services_user_prices.findOne({
where: { is_active: 1, user_id: 123 }, include:[{db.services_prices}]
});
in the db services_user_prices has foreign key to services_prices table
what am i doing wrong?
Well if you are using sequelize then you need to update your model because
by default, sequelize will be looking for foreign key starts with model name like
you have defined bundle_price_id as a foreign key for services_prices.
You need to change your column name to services_price_id then it will get fixed.
or if you want to use bundle_price_id you need to define it in your model relation as.
Model.belongsTo(models.ModelName, { foreignKey: 'your_key'} )
Please feel free if you need to ask anything else.
As complement of the above answer you need to add an identifier with as: on the association like this:
Model.belongsTo(models.ModelName, { foreignKey: 'your_key', as:'your_identifier' } )
Then when you do the include on the method you also call the identifier:
await db.services_user_prices.findOne({
where: { is_active: 1, user_id: 123 },
include:[{
model: db.services_prices
as: 'your_identifier'
}]
});
If you don't define the foreignKey field, the as field will set the column name.

how can i use limit in include model using sequelize

i want to get user's images at limit 2 from Follow model.
Models
const Follow = connector.define('Follow', {
no: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
follower_id: {
type: Sequelize.INTEGER,
allowNull: true
},
target_id: {
type: Sequelize.INTEGER,
allowNull: true
},
isDelete: {
type: Sequelize.BOOLEAN,
allowNull: false
},
create_dt,
delete_dt
}
const User = connector.define('User', {
no: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
profile_img: {
type: Sequelize.STRING,
allowNull: true
},
bio: {
type: Sequelize.STRING,
allowNull: true
},
phone: {
type: Sequelize.STRING,
allowNull: true
},
gender: {
type: Sequelize.STRING,
allowNull: true
},
website: {
type: Sequelize.STRING,
allowNull: true
},
isDelete: {
type: Sequelize.BOOLEAN,
allowNull: false
},
create_dt,
update_dt,
delete_dt
}
const Image = connector.define('Image', {
no: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
file: {
type: Sequelize.STRING,
allowNull: false
},
location: {
type: Sequelize.STRING,
allowNull: true
},
caption: {
type: Sequelize.STRING,
allowNull: true
},
tags: {
type: Sequelize.STRING,
allowNull: true
},
isDelete: {
type: Sequelize.BOOLEAN,
allowNull: false
},
create_dt,
update_dt,
delete_dt,
user_id: {
type: Sequelize.INTEGER,
allowNull: true
}
}
and, join
User.hasMany(Image, {foreignKey: 'user_id'})
Image.belongsTo(User, {foreignKey: 'user_id'})
User.hasMany(Follow, {foreignKey: 'follower_id'})
Follow.belongsTo(User, {foreignKey: 'follower_id'})
User.hasMany(Follow, {foreignKey: 'target_id'})
Follow.belongsTo(User, {foreignKey: 'target_id'})
so, i tried get user's images from follow by use include.
const followerImages = await Follow.findAll({
attributes: ['target_id'],
where:{
follower_id: loginUser_id
},
include:[
{
model: User,
required: true,
attributes: ['username', 'email', 'profile_img'],
include:[
{
model: Image,
required: true
}
]
}
]
})
but i want to get images at limit 2.
so i tried that
const followerImages = await Follow.findAll({
attributes: ['target_id'],
where:{
follower_id: loginUser_id
},
include:[
{
model: User,
required: true,
attributes: ['username', 'email', 'profile_img'],
include:[
{
model: Image,
required: true,
limit: 2
}
]
}
]
})
but it makes bugs i cant understand.
images field is a array contain empty object at 4.
all same..
what is the problem?
how can i solve this problem??
You can try :
include:[
{
model: Image,
attributes : ['id','user_id','image'] , // <---- don't forget to add foreign key ( user_id )
separate : true, // <--- Run separate query
limit: 2
}
]
Limit causes the issues some time on nested level , so it always safe to run that query separately.

Sequelize error : cannot find property "startYear" of undefined

I am using sequelize cli and got each migration file for each model generated, I am trying to generate all the tables with a single migration file as they are dependent on each other, but I am keep getting this error
cannot find property startYear of undefined
Also, I need a tutorial on how to change models in sequelize using sequelize cli.
"use strict";
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface
.createTable("Users", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
googleId: {
allowNull: true,
type: Sequelize.STRING
},
facebookId: {
allowNull: true,
type: Sequelize.STRING
},
fullName: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: Sequelize.STRING,
allowNull: false,
validate: {
isAlphanumeric: true,
notEmpty: true
}
},
isVerified: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
})
.then(function() {
return queryInterface
.createTable("DesignationMasters", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
designationName: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
})
.then(function() {
return queryInterface
.createTable("companyDetails", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
companyName: {
type: Sequelize.STRING,
allowNull: true,
defaultValue: null
},
userId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "Users",
key: "id"
}
},
designationId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "DesignationMasters",
key: "id"
}
},
startYear: {
type: Sequelize.INTEGER,
validate: {
isNumeric: true,
len: [4, 4]
},
defaultValue: null
},
endYear: {
type: Sequelize.INTEGER,
validate: {
isNumeric: true,
len: [4, 4],
min: this.startYear
},
defaultValue: null
},
isCurrentWorkplace: {
type: Sequelize.BOOLEAN,
defaultValue: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
})
.then(function() {
return queryInterface
.createTable("InterestMasters", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
interestValue: {
allowNull: false,
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
})
.then(function() {
return queryInterface.createTable("UserInterests", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
references: {
model: "Users",
key: "id"
},
allowNull: false
},
interestId: {
type: Sequelize.INTEGER,
references: {
key: "InterestMasters",
value: "id"
},
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
});
});
});
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable("InterestMasters").then(function() {
return queryInterface.dropTable("Interests").then(function() {
return queryInterface.dropTable("companyDetails").then(function() {
return queryInterface
.dropTable("DesignationMasters")
.then(function() {
return queryInterface.dropTable("Users");
});
});
});
});
}
};
Use a validate function in the options argument:
{
validate: {
endYearIsAtLeastStartYear() {
if (this.endYear < this.startYear) {
throw new Error('End year must be equal to or higher than start year')
}
}
}

Sequelize saving many to many

I was wandering if there are any extended tutorials on how to save a many to many relationship? I find the documentation more than basic. Its missing many use case examples.
I have two models: Client and Rule. They have the n:n relationship.
Client:
var Client = sequelize.define('client', {
title: {
type: DataTypes.STRING(200),
allowNull: false
},
company: {
type: DataTypes.STRING(200),
allowNull: false
},
vendor: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
consumer: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true
},
address_id: {
type: DataTypes.INTEGER,
allowNull: true
}
},{
paranoid: true,
underscored: true,
classMethods: {
associate:function(models){
Client.hasMany(models.rule, { through: 'client_rules', onDelete: 'cascade'});
}
}
});
Rule:
var Rule = sequelize.define('rule', {
service_id: {
type: DataTypes.INTEGER,
allowNull: false
},
is_allowed: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
valid_until: {
type: DataTypes.DATE,
allowNull: true,
},
rule: {
type: DataTypes.TEXT,
allowNull: true
},
type: {
type: DataTypes.INTEGER, // 1 for company rule, 2 for individual rule
allowNull: false,
},
active: {
type: DataTypes.BOOLEAN,
defaultValue: true
}
},{
underscored: true,
paranoid: true,
classMethods: {
associate:function(models){
Rule.belongsToMany(models.client, { through: 'client_rules', onDelete: 'cascade'});
Rule.belongsTo(models.service, { foreignKey: 'service_id' } );
}
}
});
Now I would like to create a new rule for client. So I would have to create the rule first and associate it then to the client through 'client_rules'.
How do I do that with sequelize? This doesn't work:
var clientID = req.user.client_id;
Client.find({ id: clientID })
.then(function(client){
return client.addRule(req.body)
})
.catch(function(err){
console.log(err)
})
[TypeError: Cannot read property 'replace' of undefined]
Ok I found out how to do it. The docs are very confusing.
var clientID = req.user.client_id;
return Rule.create(req.body)
.then(function(newRule){
var ruleToAdd = newRule;
return Client.findOne({ where: { id: clientID } })
.then(function(client){
return client.addRule(ruleToAdd)
.then(function(ans){
return ruleToAdd;
})
})

Resources