Sequelize.js belongsToMany instance method create not working? - javascript

My models:
'use strict';
module.exports = function(sequelize, DataTypes) {
var Entity;
return Entity = sequelize.define('Entity', {
name: {
type: DataTypes.STRING,
allowNull: false
}
}, {
classMethods: {
associate: function(models) {
return Entity.belongsToMany(models.User);
}
}
});
};
and
'use strict';
module.exports = function(sequelize, DataTypes) {
var User;
return User = sequelize.define('User', {
name: {
type: DataTypes.STRING,
allowNull: false
},
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
},
status: {
type: DataTypes.ENUM('active', 'inactive'),
defaultValue: 'active'
}
}, {
instanceMethods: {
display: function() {
var user;
user = {
name: this.name,
email: this.email,
username: this.username,
active: this.active
};
return user;
}
}
}, {
classMethods: {
associate: function(models) {
return User.belongsToMany(models.Entity);
}
}
});
};
I want to create a user and then attach one entity to that user, so I am doing:
newUser = {
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: req.body.password
};
global.db.User.create(newUser).then(function(dbUser) {
var newEntity;
newEntity = {
name: newUser.name + " Default Entity"
};
console.log(dbUser);
return dbUser.createEntity(newEntity);
}).then(function(dbEntity) {
return console.log(dbEntity);
});
But I get an error: [TypeError: Object [object SequelizeInstance] has no method 'createEntity']
This is using Sequelize v2.0.0-rc8
What am I doing wrong?

dbUser is an instance, so dbUser.createEntity() is calling the instance method createEntity() on the dbUser instance. Which doesn't exist.
The correct solution is to call create on your Entity model, with the UserId field set to dbUser.id. So, something like:
global.db.Entity.create({name: 'blah blah blah', UserId: dbUser.id})
(you may have to fiddle around with the capitalization).
Also, I think you may want User.hasMany(models.Entity), but that's just something that caught me off guard.

Related

Sequelize bulkCreate not including newly added columns

I'm having difficulty understanding why bulkCreate will not include my two newly created columns, perhaps it's the migration?
My new migration is as follows:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
queryInterface.addColumn('users', 'brand_id', {
allowNull: true,
type: Sequelize.INTEGER,
defaultValue: null,
});
queryInterface.addColumn('users', 'store_id', {
allowNull: true,
type: Sequelize.INTEGER,
defaultValue: null,
});
return true;
},
down: (queryInterface, Sequelize) => {
queryInterface.removeColumn('users', 'brand_id');
queryInterface.removeColumn('users', 'store_id');
return true;
}
};
I have a helper function for creating multiple users for the purpose of testing that looks something like this:
const properties = { brand_id: 123 };
const user = [];
users.push(Object.assign({}, {
name: chance.last(),
email: chance.email(),
password,
access_key: uuid(),
}, properties));
const newUsers = await models.user.bulkCreate(users, { returning: true, logging: console.log });
The output of the logging is:
INSERT INTO "users" ("id","name","email","password","access_key","created_at","updated_at") VALUES (DEFAULT,'Valente','letubdo#iwefa.fm','$2a$08$B5riQzA82ChwuH1q8HpGxOBK2uQj2m.BiHcEjytiox5yD.8u1fT5W','e62bf96c-0117-490f-9c80-b60e406238b0','2018-09-25 18:30:04.666 +00:00','2018-09-25 18:30:04.666 +00:00') RETURNING *;
You'll see that brand_id is completely ignored in the query, even if I change the following:
users.push(Object.assign({}, {
name: chance.last(),
email: chance.email(),
password,
access_key: uuid(),
brand_id: 123,
}, properties));
Any idea what could be wrong?
It turns out I did not add the two new columns to the user model:
'use strict';
module.exports = (sequelize, DataTypes) => {
var user = sequelize.define('user', {
name: {
allowNull: false,
type: DataTypes.STRING
},
email: {
allowNull: false,
type: DataTypes.STRING
},
password: {
allowNull: false,
type: DataTypes.STRING
},
access_key: {
allowNull: false,
type: DataTypes.STRING
},
brand_id: {
allowNull: true,
true: DataTypes.INTEGER,
},
store_id: {
allowNull: true,
true: DataTypes.INTEGER,
}
}, {
underscored: true,
});
return user;
};

fliped foreign key relations?

i have a strange effekt at a m:n relation..
this are the model definitions:
Role Model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Role = sequelize.define('Role', {
uuid: {
allowNull: false,
primaryKey: true,
unique: true,
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
},
....
}, {});
/** #param models.User */
Role.associate = function(models) {
Role.belongsToMany(
models.User, {
through: 'user_role',
foreignKey: 'userId',
}
);
};
return Role;
};
User Model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
uuid: {
allowNull: false,
primaryKey: true,
unique: true,
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
},
....
}, {});
/** #param models.Role */
User.associate = function(models) {
User.belongsToMany(
models.Role, {
through: 'user_role',
foreignKey: 'roleId',
}
);
};
return User;
};
the migration is the following:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('user', {
uuid: {
allowNull: false,
unique: true,
primaryKey: true,
type: Sequelize.UUIDV4,
defaultValue: Sequelize.UUIDV4,
},
....
}).then(() => {
queryInterface.createTable('role', {
uuid: {
allowNull: false,
unique: true,
primaryKey: true,
type: Sequelize.UUIDV4,
defaultValue: Sequelize.UUIDV4,
},
....
});
}).then(() => {
queryInterface.createTable('user_role', {
userId: {
type: Sequelize.UUIDV4,
references: {
model: 'User',
key: 'uuid',
},
allowNull: false,
},
roleId: {
type: Sequelize.UUIDV4,
references: {
model: 'Role',
key: 'uuid',
},
allowNull: false,
},
....
});
}).then(() => {
return queryInterface.addConstraint('user_role', ['UserId', 'RoleId'], {
unique: true,
type: 'primary key',
name: 'userrole_pkey',
});
});
},
down: (queryInterface, Sequelize) => {
....
},
};
if i try to insert now a user with a new role:
let models = require('../models');
models.Role.create({
role: 'Administrator',
description: 'Administrator Gruppe',
}).then(role => {
models.User.create({
login: 'admin',
password: '123',
nick: 'Admini',
mail: 'admin#localhost.com',
}).then(user => {
user.addRole(role);
user.save().then(() => {
console.log('admin created');
}).catch(err => {
console.log(err);
});
}).catch(err => {
console.log(err);
});
}).catch(err => {
console.log(err);
});
it tries to add the role uuid in the userid and the user uuid in the roleid.. and for that the constraint fails...
any hints or tips where i made a mistake?
found the mistake myself (with help of a college)
at
models.User, {
through: 'user_role',
foreignKey: 'userId',
}
i set the wrong foreign key, it's not the field in the helper table, it's needed to be the source table (in this case uuid of user model) or leave it blank for sequelize's default behaviour to use the primary key.

Sequelize: Before Create Hook is Not Working As Intended

I'm trying to perform a beforeCreate operation on a model that I created via Sequelize. I'm trying to have the password and salt saved before creating the user. However, the user is created without the encrypted password or salt. I'm not too familiar with Node.JS but I'm assuming this has to do with it's asynchronous nature. Any idea how to properly introduce a callback so that my create function behaves as intended?
Model:
'use strict';
var Promise = require("bluebird");
var bcrypt =Promise.promisifyAll(require("bcrypt-nodejs"));
const SALT_ROUNDS = 10;
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('User', {
username: { type: DataTypes.STRING, unique: true, allowNull: false, validate: { notEmpty: true } },
email: { type: DataTypes.STRING, unique: true, allowNull: false, isEmail: true },
phone_number: DataTypes.STRING,
password_hash: { type: DataTypes.STRING, allowNull: false, unique: true, validate: { notEmpty: true } },
password_salt: DataTypes.STRING,
first_name: DataTypes.STRING,
last_name: DataTypes.STRING,
user_type: DataTypes.INTEGER,
two_factor_enabled: { type: DataTypes.BOOLEAN, defaultValue: false, },
email_verified: DataTypes.DATE,
active: { type: DataTypes.BOOLEAN, defaultValue: true, },
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
},
validPassword: function(password, passwd, callback) {
bcrypt.compare(password, passwd, function(err, isMatch) {
if (isMatch) {
return callback(null, true);
} else {
return callback(null, false);
}
});
},
},
hooks: {
beforeCreate: function(user, {}) {
bcrypt.genSalt(SALT_ROUNDS, function(err, salt) {
bcrypt.hash(user.password_hash, salt, function(){}, function(err, hash) {
if (err) {
return sequelize.Promise.reject(err);
}
user.setDataValue('password_hash',hash);
user.setDataValue('password_salt',salt);
});
});
}
},
instanceMethods: {
generateHash: function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(10), null);
},
validPassword: function(password) {
return bcrypt.compareSync(password, this.password);
}
}
});
//User.associate = (models) => {
// User.hasMany(models.UserType, {
// foreignKey: 'userId',
// as: 'userTypes'
// });
//};
return User;
};
And here's the call:
return db.User
.create({
first_name: req.body.first_name,
last_name: req.body.last_name,
username: req.body.username,
email: req.body.email,
password_hash: req.body.password
})
.then(user => res.status(201).send(user))
.catch(error => res.status(400).send(error));
},
Personnaly I use the crypto, included in node.js : https://nodejs.org/api/crypto.html
I have a function to create the hash :
function createHash(password, salt) {
const generatedSalt = typeof salt !== 'undefined' ? salt : crypto.randomBytes(128).toString('base64');
const hmac = crypto.createHmac('sha256', generatedSalt);
hmac.update(password);
const hashedPassword = hmac.digest('hex');
return {
salt: generatedSalt,
hash: hashedPassword
};
}
And in my user model :
beforeCreate: (user, options, cb) => {
const saltAndHash = createHash(user.password);
user.salt = saltAndHash.salt;
user.password = saltAndHash.hash;
return cb(null, options);
}
I hope this will help you ;)

Sequelize how to join 2 tables 1:N

I have 2 models: User and Foto
Each User has a lot of fotos, and each foto can have just 1 user related.
To do that i use include, the problem is, i can use the include just when i am querying the user and not when i query the foto.
I get there is no relationshop between User and foto problem.
So at the moment i have this:
Model User:
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
var Foto = require('./Foto');
module.exports = function (sequelize, DataTypes) {
var User = sequelize.define("User", {
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isUnique: function (value, next) {
var self = this;
User.find({ where: { username: value } })
.then(function (user) {
// reject if a different user wants to use the same username
if (user && self.id !== user.id) {
return next('username already in use!');
}
return next();
})
.catch(function (err) {
return next(err);
});
}
}
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isUnique: function (value, next) {
var self = this;
User.find({ where: { email: value } })
.then(function (user) {
// reject if a different user wants to use the same email
if (user && self.id !== user.id) {
return next('Email already in use!');
}
return next();
})
.catch(function (err) {
return next(err);
});
}
}
},
typeOfUser: {
type: DataTypes.INTEGER,
allowNull:false,
defaultValue:2
},
country: {
type: DataTypes.STRING,
allowNull:true,
defaultValue:null
},
birthDate:{
type: DataTypes.DATEONLY,
allowNull:true,
defaultValue:null
},
reports: {
type: DataTypes.INTEGER,
defaultValue: 0
},
points: {
type: DataTypes.INTEGER,
defaultValue: 0
},
password: {
type: DataTypes.STRING,
allowNull:false
},
numberFotos: {
type: DataTypes.INTEGER,
defaultValue: 0
}
}, {
classMethods: {
generateHash: function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
associate: function(models){
User.hasMany(models.Foto,{foreignKey: "userId", as: "Fotos"});
}
},
instanceMethods: {
validPassword: function (password) {
return bcrypt.compareSync(password, this.password);
}
}
});
return User;
}
Model Foto:
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
var User = require('./User');
module.exports = function (sequelize, DataTypes) {
var Foto = sequelize.define("Foto", {
reports: {
type: DataTypes.INTEGER,
defaultValue: 0
},
image: {
type: DataTypes.STRING,
allowNull: false
},
date: {
type: DataTypes.DATE,
allowNull: true
},
lat: {
type: DataTypes.STRING,
allowNull: true
},
lon: {
type: DataTypes.STRING,
allowNull: true
},
altitude: {
type: DataTypes.STRING,
allowNull: true
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
},
plantId: {
type: DataTypes.INTEGER,
allowNull: true
},
},
{
associate: function (models) {
Foto.belongsToMany(models.User, {as:'Users'});
}
}
);
return Foto;
}
then i try to get something like this in a json three:
[{
FotoA:{
prop1:value1,
prop2:value2,
user:{
userProp1
}
}
FotoB:{
}
}]
on my route i do the following:
allPictures: function (req, res) {
Foto.findAll({include: [{ model: User, as: "Users",where:{userId: User.id} }]})
.then(function (fotos) {
res.send(fotos);
})
},
if there is a better way to do this instad of eager loading please share it, i just need to get the userId and the username.
Thanks
I guess you defined the association wrong, as you mentioned a Foto should belong to one User.
try
Foto.belongsTo(model.User);
instead of
associate: function (models) {
Foto.belongsToMany(models.User, {as:'Users'});
}
And also there should be no need for the where clause when selecting. If your associations are defined correctly, you can simply do
Foto.findAll({include: [models.User]})

sequelize hasMany, how to use customized foreginKey

Model EmployeeView
module.exports = function(sequelize, DataTypes) {
var _this = sequelize.define('EmployeeView', {
employeeId: {
type: DataTypes.INTEGER,
field: 'code'
},
username: DataTypes.STRING,
email: {
type: DataTypes.STRING,
field: 'emailaddress'
},
department: {
type: DataTypes.STRING,
field: 'department_name'
},
departmentId: {
type: DataTypes.STRING,
field: 'departments_id'
}
}, {
timestamps: false,
freezeTableName: true,
tableName: 'employees_view',
classMethods: {
associate: function(models) {
_this.belongsTo(models.EmployeeCategory, {
foreignKey: {
name: 'employeecategories_id'
}
});
_this.hasMany(models.EmployeeFile, {
foreignKey: 'employees_code'
});
}
}
});
return _this;
};
Model EmployeeFile
module.exports = function(sequelize, DataTypes) {
var _this = sequelize.define("EmployeeFile", {
employeeId: {
type: DataTypes.INTEGER,
field: 'employees_code'
},
filename: {
type: DataTypes.STRING,
filed: 'filename'
},
employeeFileTypeId: {
type: DataTypes.INTEGER,
field: 'employee_file_types_id'
}
}, {
timestamps: false,
freezeTableName: true,
tableName: 'employee_files'
});
return _this;
};
Router
router.get('/employee', function(req, res) {
models.EmployeeView.findAll({
where: {
active: req.query.active
}
include: [{
model: models.EmployeeCategory
}, {
model: models.EmployeeFile,
}]
}).then(function(employee) {
res.json(employee);
});
});
What do I expect to happen?
I have two tables 'employee_view' (it is a view) and 'employee_files' which map to the 'EmployeeView' and 'EmployeeFile'. 'employee_view' has 'id' field as the primary key and 'code' field as the employee number.'employee_files' has 'employees_code' as its primary key and foreignKey which bindings with the 'code' field. So I want to get 'employee_files' data through this relation.
What is actually happening?
Actually,I got nothing. Because the sequelize will execute "EmployeeView.id == EmployeeFile.employees_code". But I want the sequelize to execute "EmployeeView.code == EmployeeFile.employees_code" .What should I do?
just add primaryKey: true to your employeeId field to link the EmployeeView to EmployeeFiles since the belongsToMany relationship will only link to the primary key of it's parent
employeeId: {
type: sequelize.INTEGER,
field: 'code',
primaryKey: true
},

Categories

Resources