How to reference an existing model instance while creating other model instance? - node.js

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

Nodejs Sequelize Many to many relationship

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`);
}
});
})

how to not update column if value is not present sequelize

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);

Sequelize.js - How to get data from many-to-many association without defined Relation table

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']
}]
});

How to build model with sequelize for belong to many association

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'
}
})
...

load items where relation is null in sequelize

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

Resources