Sequelize belongs to many get and post request - mysql

I have belongs to many associations of the model Technology and Project. At the same time, on the client, I have two tables, free technologies and technologies on the project. Please tell me (or suggest how to do it) how to make a get request for all free technologies and a post request to add them to the table on the project. I figured out all the associations, but stopped at this one. I will be grateful for any help.
models/Project.js
const {
Model
} = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Project extends Model {
static associate(models) {
Project.hasMany(models.Role, { foreignKey: "projectId", as: "roles" });
Project.belongsToMany(models.Technology, { foreignKey: "projectId", through: "ProjectsTechnologies"});
}
};
Project.init({
title: DataTypes.STRING,
description: DataTypes.STRING,
image: DataTypes.STRING
}, {
sequelize,
modelName: "Project",
});
return Project;
};
models/Technology.js
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Technology extends Model {
static associate(models) {
Technology.belongsToMany(models.Project, { foreignKey: "technologyId", through: "ProjectsTechnologies"});
}
}
Technology.init({
name: DataTypes.STRING
}, {
sequelize,
modelName: 'Technology',
});
return Technology;
};
*models/ProjectsTechs
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class ProjectsTechnologies extends Model {
static associate(models) {
ProjectsTechnologies.belongsTo(models.Project, {foreignKey: "projectId"});
ProjectsTechnologies.belongsTo(models.Technology, {foreignKey: "technologyId"});
}
};
ProjectsTechnologies.init({
projectId: DataTypes.STRING,
technologyId: DataTypes.STRING
}, {
sequelize,
modelName: 'ProjectsTechnologies',
});
return ProjectsTechnologies;
};
Currently I'm doing this get request for get all free technologies
router.get("/techologies", async (req, res) => {
const listOfTech = await Technology.findAll({
include: [
{
model: Project,
as: "projects",
through: {
model: ProjectsTechnologies
}
}
]
});
res.json(listOfTech);
});
And post req for posting technologies in "On project" table
router.post("/create/:id", async (req, res) => {
const technology = await ProjectsTechnologies.create(req.body);
const project = await Project.findOne({
where: {
id: req.params.id
}
});
res.json(technology);
});
I also keep trying to do it differently, because these options don't seem right to me.

To query a list of technologies that are not used in any of projects you need to use a subquery in where option:
const listOfTech = await Technology.findAll({
where: Sequelize.literal('NOT EXISTS (SELECT 1 FROM `ProjectsTechnologies` where `ProjectsTechnologies`.`technologyId`=`Technology`.`id`)')
});

Related

Getting "Duplicate Column name" when trying to add a column in sequelize using migration

I am trying to add column 'player_id' to table in a migration, but getting error 'duplicate column name'.
My migration skeleton for this: -
module.exports = {
up (queryInterface, Sequelize) {
return queryInterface.sequelize.transaction(t => {
return queryInterface.addColumn('Attackings', 'player_id',{
type: Sequelize.DataTypes.INTEGER
}, { transaction: t });
});
},
down (queryInterface, Sequelize) {
return queryInterface.sequelize.transaction(t => {
return queryInterface.removeColumn('Attackings', 'player_id', { transaction: t });
});
}
};
My Attacking Model :-
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Attacking extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
Attacking.init({
player_name: DataTypes.STRING,
club: DataTypes.STRING,
position: DataTypes.STRING,
assists: DataTypes.INTEGER,
corner_taken: DataTypes.INTEGER,
offsides: DataTypes.INTEGER,
dribbles: DataTypes.INTEGER,
match_played: DataTypes.INTEGER
}, {
sequelize,
modelName: 'Attacking',
});
return Attacking;
};
As we can see column player_id doesn't already exists in table.
Error I am getting:- ERROR: Duplicate column name 'player_id'

Sequelize is searching table in plural form when calling from ManytoMany relationship

My 'House' table is in singular form and I want to get the data using ManytoMany Relationship but sequelize is considering 'House' table as 'Houses' and cannot fetch the results(i.e []) calling this like,
db.V1_DB.models.Users.findAll(
include: [
{
model: db.V1_DB.models.House,
}
]
}).then(response => {
console.log(response)
}
).catch((ex) => {
console.log("Error is here: ", ex)
})
But successfully fetches the results of House with Users,
db.V1_DB.models.House.findAll(
include: [
{
model: db.V1_DB.models.Users,
}
]
}).then(response => {
console.log(response)
}
).catch((ex) => {
console.log("Error is here: ", ex)
})
I have already added freezeTableName: true
My models are like this.
'use strict';
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class House extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
House.belongsToMany(models.Users, {
through: "Users_Has_House",
foreignKey: "Users_fk",
});
}
}
House.init({
name: DataTypes.STRING,
}, {
sequelize,
modelName: 'House',
freezeTableName: true,
name: {
singular: "House",
plural: "House"
}
});
return House;
};
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Users extends Model {
//No Associations
static associate(models) {
// define association here
Users.belongsToMany(models.House, {
through: "Users_Has_House",
foreignKey: "House_fk",
});
}
};
Users.init({
Bio: DataTypes.TEXT,
}, {
sequelize,
modelName: 'Users',
});
return Users;
};
You need to correct associations because foreignKey option should indicate to a model whose belongToMany method we are calling and otherKey should indicate to a model that is passed as a first argument to belongToMany:
House.belongsToMany(models.Users, {
through: "Users_Has_House",
foreignKey: "House_fk",
otherKey: "Users_fk"
});
Users.belongsToMany(models.House, {
through: "Users_Has_House",
foreignKey: "Users_fk",
otherKey: "House_fk"
});

Nodejs Sequelize and passport Js, stuck on executing query when registing user

this is the problem I am having:
I have 3 models(users,favorites,cryptocoins)
'use strict';
const { Model} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Cryptocoin extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
Cryptocoin.init({
coinId:{
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
coinName: DataTypes.STRING,
coinPrice: DataTypes.INTEGER,
coinAmount: DataTypes.INTEGER,
totalValue: DataTypes.STRING,
boughtOn: DataTypes.DATE
}, {
sequelize,
modelName: 'Cryptocoin',
});
return Cryptocoin;
};
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Favorite extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
Favorite.init({
favoriteId: {
type:DataTypes.INTEGER,
primaryKey:true,
autoIncrement:true,
},
userId:{
type:DataTypes.INTEGER,
references:{
model:'Users',
key:'userId',
},
onDelete:'CASCADE',
},
coinId:{
type:DataTypes.INTEGER,
references:{
model:'Cryptocoins',
key:'coinId',
},
onDelete:'CASCADE',
},
}, {
sequelize,
modelName: 'Favorite',
});
return Favorite;
};
'use strict';
const { Model} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
}
}
User.init({
userId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
userName: DataTypes.STRING,
passWord: DataTypes.STRING
}, {
sequelize,
modelName: 'User',
});
return User;
};
These all have migrations which work.
Now I have passport.js file which holds the passport stategies
const bcrypt = require('bcrypt');
module.exports = function (passport, Auth) {
const LocalStrategy = require('passport-local').Strategy;
passport.use('local-signup', new LocalStrategy(
{
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
}, function (req, username, password, done) {
console.log("Signup for - ", username)
const generateHash = function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
}
Auth.findOne({
where: {
userName: username
}
}).then(function (user) {
//console.log(user);
if (user) {
return done(null, false, {
message: 'That username is already taken'
});
} else {
const userPassword = generateHash(password);
const data = {
username: username,
password: userPassword,
};
Auth.create(data).then(function (newUser, created) {
if (!newUser)return done(null, false);
if (newUser) return done(null, newUser)
});
}
});
}
));
This file is called via app.js like so
just a snippet of the code in app.js
const models = require('./models');
app.use(session({
genid: (req) => {
return uuid.v1();
},
name: 'Crypto-session',
store:new fileStore(),
secret: '------',
resave:false,
saveUninitialized:false,
}))
app.use(passport.initialize());
app.use(passport.session());
require('./config/passport')(passport,models.User);
Now when going to my /register route and inputing a username and password this is what happens
Signup for - randomUser
Executing (default): SELECT `userId`, `userName`, `passWord`, `createdAt`, `updatedAt` FROM `Users` AS `User` WHERE `User`.`userName` = 'randomUser' LIMIT 1;
Executing (default): INSERT INTO `Users` (`userId`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?);
And it's stuck at the executing (default) part, It gets uploaded to the database but without any username or password, console logging both of them shows they are being populated with values from my form.
Any idea how I can fix this issue ?
note: I am fairly new to express and nodeJs in general.
Answer: After brainstorming and reading docs a bit these where the issues.
Ensure the deserializeUser function is either await or then based e.g
// deserialize user
passport.deserializeUser(function (id, done) {
Auth.findByPk(id).then(function (user) {
if (user) {
done(null, user.get());
} else {
done(user.errors, null);
}
});
});
Ensure you have a redirect in you route.post method e.g
// route for register action
app.post('/register',passport.authenticate('local-signup',{ successRedirect: '/',
failureRedirect: '/register' }),function (req, res) {
});
Ensure all of your passport js fields match the database fields e.g
I had username and password, but in my database it is userName and passWord.
changing these like so
const data = {
userName: username,
passWord: userPassword,
};
Uploads everything to the database.

Select parent model with child model attribute and destroy it - sequelize/MySQL

I have User and VerifyToken model with token and user_id attributes.
I need to select an user, but with token (from tokens table) value.
I have tried:
await models.User.destroy({
hierarchy: true,
where: {
token: token
}
});
This is User model:
const VerifyToken = require('./verifyToken');
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
password: DataTypes.STRING,
email: DataTypes.STRING,
}, {
tableName: 'users',
//syncOnAssociation: true
hierarchy: true
});
User.associate = function (models) {
const {VerifyToken} = models;
User.hasOne(VerifyToken, {
onDelete: 'CASCADE'
});
};
return User;
};
And VerifyToken model:
const User = require('./user');
module.exports = (sequelize, DataTypes) => {
const VerifyToken = sequelize.define('VerifyToken', {
user_id: DataTypes.INTEGER,
token: DataTypes.STRING,
}, {
tableName: 'verify_tokens',
syncOnAssociation: true,
hierarchy: true
});
VerifyToken.associations = function (models) {
const {User} = models;
VerifyToken.belongsTo(User);
};
return VerifyToken;
};
The problem is that, I even don't know where to start. I have tried with include:[{model: models.VerifyToken, where: {}}], but how to use user_id called from the child model ?
What I want is to select an user (parent model) with a value (token in child model) and delete it with one query.
The problem statement you want is to support join and delete in one sequelize operation.
What I want is to select an user (parent model) with a value (token in child model) and delete it with one query.
In sequelize documenation, Model.destroy has no include in the options property.
So the only left option is to select the user_id's from VerifyToken model, then call destroy on User model, where id in user_id got from VerifyToken.
In code it will look like following
const verifyTokens = await VerifyToken.findAll({
where: {
token: {
[Sequelize.Op.In] : YOUR_TOKENS_FOR_WHICH_YOU_WANT_TO_DELETE_YOUR_USER
}
}
}
const userIdsToDestroy = verifyTokens.map(verifyToken => verifyToken.user_id)
await User.destroy({
where: {
id: {
[Sequelize.Op.in] : userIdsToDestroy
}
}
}

Creating row in both source and association with Sequelize many to many model

I have a many to many relationship between a user and a group. A user has many groups and a group has many users.
I have a users table, a groups table and junction table called usersGroups.
My User model:
'use strict';
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('User', {
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
email: DataTypes.STRING,
username: DataTypes.STRING,
facebookId: DataTypes.STRING,
password: DataTypes.STRING,
}, {
classMethods: {
associate: function(models) {
User.hasMany(models.Payment);
User.hasMany(models.Friend, {foreignKey: 'userIdLink1', allowNull: false});
User.belongsToMany(models.Group, { as: 'Groups', through: 'usersGroups', foreignKey: 'userId' });
}
},
instanceMethods: {
toJSON: function () {
var values = Object.assign({}, this.get());
delete values.password;
return values;
}
}
});
return User;
};
My group model
'use strict';
module.exports = function(sequelize, DataTypes) {
var Group = sequelize.define('Group', {
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
Group.belongsToMany(models.User, { as: 'Users', through: 'usersGroups', foreignKey: 'groupId' });
}
},
instanceMethods: {
toJSON: function () {
var values = Object.assign({}, this.get());
delete values.password;
return values;
}
}
});
return Group;
};
When I try create a new group with associated user with the following 2 methods, it creates a new group but no association
const values = {
userId: 1
}
const options = {
include: db.Users
}
db.Group
.create(values, options)
.then( (group) => {
res.send(group)
}).catch(err => {
res.status(500).send({err: err})
})
or
db.Group
.create()
.then( (group) => {
group.addUser({ userId: 1 }).then(result => {
res.send(result)
}).catch(err => {
res.status(500).send({err: err})
})
}).catch(err => {
res.status(500).send({err: err})
})
If you simply want to assign user to newly created group, you need to use addUser, just like you did, but this method accepts first parameter as instance of Model or ID of instance, just like the documentation says
An instance or primary key of instance to associate with this.
So you would have to perform group.addUser(userId).then(...) which in your case would be group.addUser(1).then(...).