Sequelize null value in column violates not-null constraint - node.js

I'm getting this error when trying to associate a like to a post.
Unhandled rejection SequelizeDatabaseError: null value in column
"userId" violates not-null constraint
Now the following code gets the post id and user id ok, i did a console log. What could i be doing wrong ?
routes/posts.js
router.post('/:userId/like/:postId', (req, res)=> {
models.Post.findOne({
where:{
id: req.params.postId
}
})
.then( (like) => {
if(like){
models.Likes.create({
where:{
userId: req.params.userId,
postId: req.params.postId
},
like:true
}).then( (result) => {
res.status(200).send({
message: 'You have like this post',
like: result
})
})
}
}).catch( (err) => {
res.status(401).send({
message: "Something went wrong",
err: err
})
})
})
here is the likes migration
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Likes', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
like: {
type: Sequelize.BOOLEAN
},
userId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Likes');
}
};
Posts migration
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Posts', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
title: {
type: Sequelize.STRING
},
post_content: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
}
},
username: {
type: Sequelize.STRING
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Posts');
}
};
Like model
'use strict';
module.exports = function(sequelize, DataTypes) {
const Like = sequelize.define('Likes', {
like:{
type:DataTypes.BOOLEAN,
allowNull:true
}
}, {});
Like.associate = function(models) {
Like.belongsTo(models.User, {
onDelete: "CASCADE",
sourceKey: 'userId'
})
Like.belongsTo(models.Post, {
onDelete: "CASCADE",
sourceKey: 'likeId'
})
}
return Like;
}
Post.model
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
title: DataTypes.STRING,
post_content: DataTypes.STRING,
username: DataTypes.STRING
}, {});
Post.associate = function(models) {
Post.belongsTo(models.User, { foreignKey: 'userId', targetKey: 'id' });
Post.hasMany(models.Likes, { foreignKey: 'postId', sourceKey: 'id' });
};
return Post;
};
extra
add_postId_to_likes
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.addColumn(
'Likes',
'postId',
{
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'Posts',
key: 'id',
}
}
)
},
down: function (queryInterface, Sequelize) {
return queryInterface.removeColumn(
'Likes',
'postId'
)
}
};

In your create call in resolver you are not giving it the necessary values, you have a where clause but not actually giving it the value for required userId.. looks like the only value in your model is the Boolean you are setting

I figured it out.
I just used body instead of params for the postId.
router.post('/like', (req, res)=> {
models.Likes.create({
postId: req.body.postId,
userId: req.user.id,
like:true
}).then( (result) => {
res.status(200).send({
message: 'You have like this post',
like: result
});
}).catch( (err) => {
res.status(401).send({
message: "Something went wrong",
err: err
})
})
})
change my like model to this, i was using sourceKey instead of foreign keys
module.exports = function(sequelize, DataTypes) {
const Like = sequelize.define('Likes', {
like:{
type:DataTypes.BOOLEAN,
allowNull:true
},
// userId: {
// type: sequelize.INTEGER,
// references: {
// model: 'Users',
// key: 'id'
// }
// },
}, {});
Like.associate = function(models) {
Like.belongsTo(models.User, {
onDelete: "CASCADE",
foreignKey: 'userId'
})
Like.belongsTo(models.Post, {
onDelete: "CASCADE",
foreignKey: 'likeId'
})
}
return Like;
}
So now i can like a post, and it will attach the postId along with the usersId on the likes table.
like this

Related

NodeJs Join two tables using sequalize and retrieve the data in Controller

I have two model classes in separate files as below,
module.exports = (sequelize, Sequelize) => {
return sequelize.define(
"course",
{
id: {
type: Sequelize.STRING,
primaryKey: true,
field: 'ID',
},
title: {
type: Sequelize.STRING,
field: 'TITLE'
}
},
{
timestamps: false,
freezeTableName: true,
underscored: true
},
);
};
module.exports = (sequelize, Sequelize) => {
return sequelize.define(
"student",
{
id: {
type: Sequelize.STRING,
primaryKey: true,
field: 'ID',
},
courseId: {
type: Sequelize.STRING,
field: 'COURSE_ID'
}
},
{
timestamps: false,
freezeTableName: true,
underscored: true
},
);
};
And in the controller I have written like this.
const db = require("../config/sequelize.config");
const course = db.course;
const student= db.student;
student.belongsTo(course, {foreignKey: 'courseId', targetKey: 'id'});
exports.findStudentData = (req, res) => {
return student.findOne({ limit: 1 },
{
include : [{
model: course
}]}).then(data => {
res.send(data);
}).
catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred."
});
});
};
Here I need to get course data along with student data. When I ran the code it only gives me the student data without course details. I'm not sure If I have added the following statement correct
student.belongsTo(course, {foreignKey: 'courseId', targetKey: 'id'});

Can't update a specific column in Sequelize

I have a model. It is for the intermediate(pivot) table.
UserCars.init({
carId: DataTypes.INTEGER,
userId: DataTypes.INTEGER,
title: DataTypes.STRING,
}, {
timestamps: false,
sequelize,
modelName: 'UserCars',
});
and here is my migration for this:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('UserCars', {
carId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'Cars',
key: 'id'
},
},
userId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
},
},
title: {
type: Sequelize.STRING
},
}, {
uniqueKeys: {
Items_unique: {
fields: ['carId', 'userId']
}
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('UserCars');
}
};
And I'm doing this below to create/update it:
userCar = await UserCars.findOne({
where: {
carId: 10,
userId: req.user.id,
}
});
if(userCar) {
userCar.userId = 20; // <--- This doesn't change
userCar.title = 'some other thing'; // <--- This changes
await userCar.save();
} else {
userCar = await UserCars.create({
userId: 20,
title: 'something'
});
}
The problem is, the title is being updated but the userId is not.
(I believe) this is due to constraints, you cannot use instance method to update FK value.
You need to use M-N association functions, or otherwise you could use raw SQL.
const car = await Car.findByPk(10);
const user = await User.findByPk(newValue);
// This also takes care of deleting the old associations
await car.setUsers(user, {
through: {'title': 'new value'}
});
I hope the upsert function is implemented in the future.
ref: https://github.com/sequelize/sequelize/issues/11836

Sequelize especial methods don't get created when Using sequelize migrations

I am struggling when using sequelize migrations and a many-to-many relationship between Users and Roles.
This is the Users model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const user = sequelize.define('Users', {
username: DataTypes.STRING,
name: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {});
user.associate = function(models) {
// associations can be defined here
user.belongsToMany(models.Roles, {
through: models.UserRoles
});
};
return user;
};
This is the Roles model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const role = sequelize.define('Roles', {
name: DataTypes.STRING
}, {});
role.associate = function(models) {
// associations can be defined here
role.belongsToMany(models.Users, {
through: models.UserRoles
});
};
return role;
};
This is the "create-user" migration:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
username: {
type: Sequelize.STRING,
allowNull: true,
len: [0, 20]
},
name: {
type: Sequelize.STRING,
allowNull: true,
},
email: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
This is the "create-role" migration:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Roles', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Roles');
}
};
This is the userRoles Model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const user_role = sequelize.define('UserRoles', {
userId: DataTypes.INTEGER,
roleId: DataTypes.INTEGER
}, {});
user_role.associate = function(models) {
// associations can be defined here
};
return user_role;
};
And last one the "user-roles" migration:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('UserRoles', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
primaryKey: true,
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
roleId: {
primaryKey: true,
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Roles',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('UserRoles');
}
};
The problem happens when I try to access to the user.setRoles() from a controller:
exports.signup = (req, res) => {
console.log('creating new user', req.body.username);
// Save User to Database
User.create({
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password, 8)
})
.then(user => {
console.log('USER ADDED');
if (req.body.roles) {
Role.findAll({
where: {
name: {
[Op.or]: req.body.roles
}
}
}).then(roles => {
console.log('ROLES ', roles);
user.setRoles(roles).then(() => {
res.send({ message: "User was registered successfully!" });
});
});
} else {
console.log('NO ROLES > Normal User');
// user role = 1
user.setRoles([1]).then(() => {
res.send({ message: "User was registered successfully!" });
});
}
})
.catch(err => {
console.log('ERROR: ', err);
res.status(500).send({ message: err.message });
});
};
When I console.log the user using : console.log(Object.keys(user.__proto__)) I get this array where the special methods haven't been created, any idea what I am doing wrong?
Array(7) ["_customGetters", "_customSetters", "validators", "_hasCustomGetters", "_hasCustomSetters", "rawAttributes", "_isAttribute"]
Many thanks for your help!
Just call all associate functions after registering models. For instance:
const models = path.join(__dirname, 'models')
const db = {}
fs.readdirSync(models)
.filter(function (file) {
return (file.indexOf('.') !== 0) && (file.slice(-3) === '.js')
})
.forEach(function (file) {
var model = sequelize['import'](path.join(models, file))
db[model.name] = model
})
Object.keys(db).forEach(function (modelName) {
if (db[modelName].associate) {
db[modelName].associate(db)
}
})

Can't delete all from Sequelize n:m associated table

I am trying to delete all contents from a n:m association table.
I have the tables: MenuItems and UserGroups like this, but I am using migrations to genererate the database:
MenuItems
module.exports = (sequelize, DataTypes) => {
const MenuItems = sequelize.define('MenuItems', {
title: {
type: DataTypes.STRING,
allowNull: false
}
// more fields...
}, {
freezeTableName: true
});
MenuItems.associate = function(models) {
MenuItems.belongsToMany(models.UserGroups, { through: 'MenuItemUserGroups' });
};
UserGroups
module.exports = (sequelize, DataTypes) => {
const UserGroups = sequelize.define('UserGroups', {
name: {
type: DataTypes.STRING,
unique: true
}
}, {
freezeTableName: true
});
UserGroups.associate = function(models) {
// Associations to other models
UserGroups.belongsToMany(models.MenuItems, { through: 'MenuItemUserGroups' });
};
return UserGroups;
};
The association table is generated with the following migration:
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('MenuItemUserGroups', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
menuItemId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'MenuItems',
key: 'id'
},
onUpdate: 'cascade',
onDelete: 'cascade'
},
userGroupId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'UserGroups',
key: 'id'
},
onUpdate: 'cascade',
onDelete: 'cascade'
}
//more fields
});
}
I try to delete using the following sequelize code:
models.MenuItems.findOne({ where: { id: 1 }, include: [{ all: true }] }).then(menuItem => {
if(req.body.userGroups.length <= 0) {
menuItem.setUserGroups([]).then(result => {
console.log(result);
});
The SQL that is generated is the following:
DELETE FROM `MenuItemUserGroups` WHERE `UserGroupId` = 1 AND `MenuItemId` IN (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
[ 0 ]
where UserGroupId IN (NULL) will always return nothing. If that part is removed, the query works.
Could this have something to do with some naming convention?

Create Association in Sequelize

I am using "sequelize": "^5.8.6" and have created my project structure using "sequelize-cli": "^5.4.0". I would like to create associations so that:
One company has many ratings
I have created a company model, which looks like that:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Company = sequelize.define('Company', {
name: DataTypes.STRING,
symbol: DataTypes.STRING,
}, {});
Company.associate = function(models) {
Company.hasMany(models.Rating);
};
return Company;
};
My Rating model looks like that:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Rating = sequelize.define('Rating', {
action: DataTypes.STRING,
}, {});
Rating.associate = function(models) {
Rating.belongsTo(models.Company);
// associations can be defined here
};
return Rating;
};
My Company Migration look like the following:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Companies', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
symbol: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Companies');
}
};
My Rating migration looks like the following:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Ratings', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
companyid: {
type: Sequelize.INTEGER,
references: {
model: 'Company',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
},
action: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Ratings');
}
};
When running, I get the following error:
> npx sequelize-cli db:migrate
ERROR: Can't create table `test_db`.`ratings` (errno: 150 "Foreign key constraint is incorrectly formed")
Any suggestions what I am doing wrong?
I appreciate your replies!
If you haven't just left it out of your code, your company model association should read:
Company.associate = function(models) {
Company.hasMany(models.Rating, {
foreignKey: 'companyid',
targetKey: 'id'
});
};
And your rating model should read:
Rating.associate = function(models) {
Rating.belongsTo(models.Company, {
// associations can be defined here
foreignKey: 'companyid',
targetKey: 'id'
});
};

Resources