Newbie in sequelize.js
I have two models: User and Organization and I am trying to simulate A user can be admin of one organization, organization can have only one admin (one-to-one) association using belongsTo().
Here is how models are defined:
const User = sequelize.define(
"User",
{
firstName: Sequelize.STRING,
lastName: Sequelize.STRING
},
{ underscored: true }
);
const Organization = sequelize.define(
"Organization",
{
name: Sequelize.STRING
},
{ underscored: true }
);
Organization.belongsTo(User, {
as: "admin",
foreignKey: {
allowNull: false
}
});
Here is what I want to do in sequence:
Create a User.
Create an Organization and then set the created User as the admin.
The issue:
It does not let me specify an already existing user as admin.
Here is what I tried: (By looking at documentation and other examples)
1.
// step 1
await User.create({
firstName: "John",
lastName: "Doe"
});
// step 2
const adminToSet = await User.findOne({ where: { firstName: "John" } });
await Organization.create(
{
name: "my-awesome-organization",
User: adminToSet
},
{
include: [{ model: User, as: "admin" }]
}
);
It gives me error saying 'Organization.admin_id cannot be null'
2.
// step 1
await User.create({
firstName: "John",
lastName: "Doe"
});
// step 2
const adminToSet = await User.findOne({ where: { firstName: "John" } });
const org = Organization.build({
name: "my-awesome-organization"
});
org.setAdmin(adminToSet);
It gives me error saying id = id || results && results[0][this.getInsertIdField()]; (which seems to be of the mssql -> the dialect I am using)
Please help!
Here is the complete code snippet:
const Sequelize = require("sequelize");
const connectionOptions = {
username: "sa",
password: "Test#123",
dialect: "mssql",
host: "localhost",
port: 1433,
operatorsAliases: false,
benchmark: true,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
// Dialect specific options. These will be passed to the db driver
dialectOptions: {
encrypt: false
}
};
const sequelize = new Sequelize(connectionOptions);
const User = sequelize.define(
"User",
{
firstName: Sequelize.STRING,
lastName: Sequelize.STRING
},
{ underscored: true }
);
const Organization = sequelize.define(
"Organization",
{
name: Sequelize.STRING
},
{ underscored: true }
);
Organization.belongsTo(User, {
as: "admin",
foreignKey: {
allowNull: false
}
});
const createUserAndOrganization = async () => {
// step 1
await User.create({
firstName: "John",
lastName: "Doe"
});
// step 2
const adminToSet = await User.findOne({ where: { firstName: "John" } });
const org = Organization.build({
name: "my-awesome-organization"
});
org.setAdmin(adminToSet);
};
const authenticated = async () => {
try {
await sequelize.sync({ force: true });
await createUserAndOrganization();
} catch (e) {
console.error(e);
}
};
sequelize
.authenticate()
.then(authenticated)
.catch(err => {
console.log(`[${err.name}]`, `[${err.original.code}]`, `${err.original.message}`);
});
Related
I am trying to fetch data from DB with sequelize. The many to many relationships between users and roles. When i fetch the users does not include the roles.
The code look like:
user model
// model defines the user objects
const userModel = (sequelize, Sequelize) => {
const users = sequelize.define("user", {
id: {
type: Sequelize.STRING,
allowNull: false,
primaryKey: true,
},
firstname: {
allowNull: false,
type: Sequelize.STRING,
},
lastname: {
allowNull: false,
type: Sequelize.STRING,
},
password: {
allowNull: false,
type: Sequelize.STRING,
},
email: {
allowNull: false,
type: Sequelize.STRING,
},
image: {
allowNull: true,
type: Sequelize.STRING,
},
});
//don not show password and id
users.prototype.toJSON = function () {
let values = Object.assign({}, this.get());
delete values.password;
delete values.id;
return values;
};
return users;
};
export default userModel;
Roles model
// model defines the events objects
const rolesModel = (sequelize, Sequelize) => {
const roles = sequelize.define("roles", {
id: {
type: Sequelize.STRING,
allowNull: false,
primaryKey: true,
},
name: {
allowNull: false,
type: Sequelize.STRING,
},
description: {
allowNull: true,
type: Sequelize.STRING,
},
});
return roles;
};
export default rolesModel;
The associations:
db.users.associate = (db) => {
db.users.belongsToMany(db.roles, {
through: "userroles",
constraints: false,
foreignKey: "rolesId",
});
};
db.roles.associate = (db) => {
db.roles.belongsToMany(db.users, {
through: "userroles",
constraints: false,
foreignKey: "userId",
});
};
There are two controller functions that are adding and fetching the user data
Controller
User.create(userDetails)
.then(() => {
let roles = req.body.roles;
roles.forEach(async (element) => {
let role = await Roles.findByPk(element);
if (role) {
await Userroles.create({
id: uniqid(),
rolesId: element,
userId: userId,
});
} else {
logger.warn(`tried adding to ${userId} a none existent role`);
}
});
})
// get user
let user = await User.findOne({
where: { email: username },
include: { model: db.roles },
});
So the roles are only a empty array when I try getting user details:
"firstname": "Mathew",
"lastname": "Murimi",
"email": "******#gmail.com",
"image": null,
"createdAt": "2022-02-12T22:56:40.000Z",
"updatedAt": "2022-02-12T22:56:40.000Z",
"roles": []
Receive the user created in the then, add the id of "newUser" in "userId"
User.create(userDetails)
.then((**newUser**) => {
let roles = req.body.roles;
roles.forEach(async (element) => {
let role = await Roles.findByPk(element);
if (role) {
await Userroles.create({
id: uniqid(),
rolesId: element,
userId: **newUser.id**,
});
} else {
logger.warn(`tried adding to ${**newUser.id**} a none existent role`);
}
});
})
I have a customer table which consist few columns along with password column in sequelize mysql
When I edit/update the customer if the user doesn't provide the password in the request it should not update the password column at all (should remain untouched).
How can I accomplish such program inside the customer schema page?
I am using following method to update the table:
db.customers.findOne({
where: {
id: req.body.id
}
}).then(data => {
data.update({
cash_credit: req.body.cash_credit,
name: req.body.name,
address: req.body.address,
state_id: req.body.state_id,
gstin: req.body.gstin,
mobile: req.body.mobile,
phone: req.body.phone,
email: req.body.email,
form_type: req.body.form_type,
pincode: req.body.pincode,
password: req.body.password, // omit
city_id: req.body.city_id,
country: req.body.country || 0,
id: req.body.id
}).then(data2 => {
console.log(data2);
});
});
Here is my customer schema:
const bcrypt = require("bcrypt");
module.exports = function (sequelize, DataTypes) {
const customers = sequelize.define("customers", {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
field: "SupplierCode"
},
customer_type: {
type: DataTypes.INTEGER,
},
cash_credit: {
type: DataTypes.STRING,
},
...
}, {
hooks: {
// eslint-disable-next-line no-unused-vars
beforeValidate: function (value, option) {
value.zip = parseInt(value.dataValues.zip);
},
beforeCreate: async (schema) => {
let hashedPassword = await bcrypt.hash(schema.password, saltRounds);
schema.password = hashedPassword;
console.log(schema.password);
},
beforeUpdate: async (schema) => {
if (schema.password) {
let hashedPassword = await bcrypt.hash(schema.password, saltRounds);
schema.password = hashedPassword;
}
}
},
timestamps: false,
defaultScope: {
attributes: {
exclude: ["password"]
}
},
scopes: {
withPassword: {
attributes: {
include: ["password"]
}
}
}
});
}
As far as I remember Sequelize won't update a field that you have not provided. So if the password is not defined, just don't pass it to the update call.
const updateData = {
cash_credit: req.body.cash_credit,
name: req.body.name,
address: req.body.address,
state_id: req.body.state_id,
gstin: req.body.gstin,
mobile: req.body.mobile,
phone: req.body.phone,
email: req.body.email,
form_type: req.body.form_type,
pincode: req.body.pincode,
city_id: req.body.city_id,
country: req.body.country || 0,
id: req.body.id
}
if (req.body.password) {
updateData.password = req.body.password;
}
data.update(updateData).then(console.log);
I have defined two database models in Sequelize.js:
User:
module.exports = (sequelize, DataTypes) => {
const model = sequelize.define('User', {
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
password: {
type: DataTypes.CHAR,
length: 60,
allowNull: false
}
}
});
model.associate = models => {
model.belongsToMany(models.Role, {
hooks: true,
through: 'user_roles'
})
};
return model;
};
Role:
module.exports = (sequelize, DataTypes) => {
const model = sequelize.define('Role',
{
name: {
type: DataTypes.STRING,
unique: false,
allowNull: false
}
}
);
model.associate = models => {
model.belongsToMany(models.User, {
hooks: true,
through: 'user_roles'
});
};
return model;
};
Table user_roles is create automatically. When I query for User I also want to include roles array in the result. I do so like this:
const user = await User.findOne({
where: {
email: data.email
},
attributes: ['id', 'password', 'email'],
include: [{
model: 'user_roles',
attributes: ['key']
}]
});
But I get error: TypeError: include.model.getTableName is not a function what means that model: must be real model and can't do just table name. But how to get data from this table then?
I also tried:
const user = await User.findOne({
where: {
email: data.email
},
attributes: ['id', 'password', 'email'],
include: [{
model: Role,
attributes: ['key']
}]
});
But this gives me weird object with too much data:
Roles: [
{
key: 'USER',
user_roles: {
createdAt: 2019-09-17T16:19:44.000Z,
updatedAt: 2019-09-17T16:19:44.000Z,
RoleId: 1,
UserId: 4
}
}
]
I need just the key: 'USER'
Try adding through: { attributes: [] }.
const user = await User.findOne({
where: {
email: data.email
},
attributes: ['id', 'password', 'email'],
through: { attributes: [] },
include: [{
model: Role,
attributes: ['key']
}]
});
This is what I wrote in Country.js (exactly the same as User.js except datatypes) :
module.exports = function(sequelize, DataTypes) {
const Country = sequelize.define('country',
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
code: {
type: DataTypes.INTEGER
},
alpha2: {
type: DataTypes.STRING
},
alpha3: {
type: DataTypes.STRING
},
name_en: {
type: DataTypes.STRING
},
name_fr: {
type: DataTypes.STRING
}
},
{
freezeTableName: true,
timestamps: false
});
Country.associate = ( models ) => {
models.Country.belongsToMany(models.User, {
through: 'country_user',
as: 'user',
foreignKey: 'id_country'
});
};
return Country;
}
This is my query :
router.get('/thisuserCountries', function(req, res, next){
User(db, Sequelize.DataTypes).findOne({
include: [{
model: Country(db, Sequelize.DataTypes),
as: 'countries',
required: false,
attributes: ['id'],
}],
where: {
email: 'jerome.charlat#gmail.com'
}
})
.then(user => {
if(user) {
res.json(user)
}
else {
res.send('User does not exist')
}
})
.catch(err => {
res.send('error: ' + err)
})
})
This is my db.js :
const Sequelize = require('sequelize')
const db = new Sequelize('travel_memories', 'root', '', {
host: 'localhost',
dialect: 'mysql',
port: 3306
})
db
.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.error('Unable to connect to the database:', err);
});
const models = {
Country: db.import('../models/Country'),
User: db.import('../models/User'),
CountryUserJoin: db.import('../models/Country_user')
};
Object.keys(models).forEach((modelName) => {
if('associate' in models[modelName]){
models[modelName].associate(models);
}
});
module.exports = db
Postman says : error SequelizeEagerLoadingError: country is not associated to user!
But, I think I should write in the through parameter the model User_country when I associate tables in each model. So i tried to write something like :
Country.associate = ( models ) => {
models.Country.belongsToMany(models.User, {
through: models.Country_user,
as: 'user',
foreignKey: 'id_country'
});
};
And console says when I launch server, before querying anything :
SequelizeAssociationError: country.belongsToMany(user) requires through option, pass either a string or a model.
So I am blocked. I used the example in documentation to write the assocation with models.foo. But in fact models comes from nowhere..
Thanks again for your help !
There's not a lot of documentation about this, but here it says that you should use a through option when querying or selecting belongs-to-many attributes, just like this:
...
User(db, Sequelize.DataTypes).findOne({
include: [{
model: Country(db, Sequelize.DataTypes),
as: 'countries',
required: false,
through: {
attributes: ['id']
}
}],
where: {
email: 'jerome.charlat#gmail.com'
}
})
...
I'm new to sequelize, i'm trying to load all entries in my user table where the task relation is null. but its not working. here is what i have tried:
const express = require('express');
const app = express();
const Sequelize = require('sequelize');
const sequelize = new Sequelize('sequelize', 'mazinoukah', 'solomon1', {
host: 'localhost',
dialect: 'postgres',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000,
},
});
const Task = sequelize.define('Task', {
name: Sequelize.STRING,
completed: Sequelize.BOOLEAN,
UserId: {
type: Sequelize.INTEGER,
references: {
model: 'Users', // Can be both a string representing the table name, or a reference to the model
key: 'id',
},
},
});
const User = sequelize.define('User', {
firstName: Sequelize.STRING,
lastName: Sequelize.STRING,
email: Sequelize.STRING,
TaskId: {
type: Sequelize.INTEGER,
references: {
model: 'Tasks', // Can be both a string representing the table name, or a reference to the model
key: 'id',
},
},
});
User.hasOne(Task);
Task.belongsTo(User);
app.get('/users', (req, res) => {
User.findAll({
where: {
Task: {
[Sequelize.Op.eq]: null,
},
},
include: [
{
model: Task,
},
],
}).then(function(todo) {
res.json(todo);
});
});
app.listen(2000, () => {
console.log('server started');
});
if i have three users, and 2 of those users have a task each, i want to load just the last user without a task. is this possible in sequelize ?
after much debugging i found the solution
app.get('/users', (req, res) => {
User.findAll({
where: {
'$Task$': null,
},
include: [
{
model: Task,
required: false,
},
],
}).then(function(todo) {
res.json(todo);
});
});
by adding this where clause
where: {
'$Task$': null,
},
i was able to load only users without a task