Sequelize findAll with belongsToMany association - javascript

I have two tables related in this way :
League
sequelize.define('league', {
name: {
type: DataTypes.STRING,
},
ownerId: {
type: DataTypes.INTEGER,
}
}, {
classMethods: {
associate: function(models) {
League.belongsToMany(models.user, {
constraints: false,
through: models.UserLeague,
});
}
}
}, {
freezeTableName: true
});
User
sequelize.define('user', {
name: {
type: DataTypes.STRING
},
email: {
type: DataTypes.STRING,
unique: true,
validate: {
isEmail: true
}
},
profile_picture: {
type: DataTypes.STRING
}
}, {
classMethods: {
associate: function(models) {
User.belongsToMany(models.league, {
constraints: false,
through: models.UserLeague,
});
}
}
}, {
freezeTableName: true
});
I would like to get all the leagues that have a certain User in them : I'm doing this at the moment but I get the error column league.users.id does not exist
sequelize.transaction(function (t) {
return models.user.findOne({where: {email: req.query.email}}, {transaction: t}).then(function(user) {
return models.league.findAll({where: {'users.id': user.id}, include: [{model: models.user, as: 'users'}]}).then(function(leagues) {
res.json(leagues);
});
});
});
How could I retrieve the leagues where there is a certain user ?

You should add your where to the included model:
sequelize.transaction(function (t) {
return models.user.findOne({where: {email: req.query.email}}, {transaction: t}).then(function (user) {
return models.league.findAll({
include: [{
model: models.user,
as: 'users',
where: {
id: user.id
},
required: true
}]
}).then(function (leagues) {
res.json(leagues);
});
});
});
Update:
Its' not really nice, but I thinik, there is no better solution:
sequelize.transaction(function (t) {
return models.user.findOne({where: {email: req.query.email}}, {transaction: t}).then(function (user) {
var _leagueIds = [];
return models.league.findAll({
include: [{
model: models.user,
as: 'users',
where: {
id: user.id
},
required: true
}]
})
.each(function (league) {
_leagueIds.push(league.id);
})
.then(function () {
return models.league.findAll({
where: {
id: _leagueIds,
},
include: [{
model: models.user,
as: 'users'
}]
});
})
.then(function (leagues) {
res.json(leagues);
});
});
});

Related

Checking if two values match from one to another table

So I started doing a app with JavaScript ORM- Sequelize.
I have a table called Workouts.js where the exercises are the id's in a table called Exercises.js.
Workouts.js
id
exercise_one
exercise_two
exercise_three
date
1
1
2
4
2022-02-21
2
4
3
2
2022-02-23
3
3
1
1
2022-02-25
I have another table called Workout_Volume.js which has the following values:
Workout_Volume.js
id
workout_id
exercise_id
sets
reps
weight
1
1
1
3
10
60
2
1
4
4
12
40
3
3
3
3
15
30
4
2
4
5
5
80
So my question is what is the correct way to validate in Workout_Volume.js workouit_id and exercise_id match as in Workouts.js when creating and updating Workout_Volume.js?
Workouts.js
const workout = (sequelize, DataTypes) => {
const Workout = sequelize.define(
'workouts', {
exercise_one: {
type: DataTypes.INTEGER(), allowNull: false,
references: { model: "exercises", key: "id" },
validate: {
notNull: { msg: "You need to provide exercise_id !" }
}
},
exercise_two: {
type: DataTypes.INTEGER(), allowNull: true,
references: { model: "exercises", key: "id" },
},
exercise_three: {
type: DataTypes.INTEGER(), allowNull: true,
references: { model: "exercises", key: "id" },
},
exercise_four: {
type: DataTypes.INTEGER(), allowNull: true,
references: { model: "exercises", key: "id" },
},
exercise_five: {
type: DataTypes.INTEGER(), allowNull: true,
references: { model: "exercises", key: "id" },
},
exercise_six: {
type: DataTypes.INTEGER(), allowNull: true,
references: { model: "exercises", key: "id" },
},
exercise_seven: {
type: DataTypes.INTEGER(), allowNull: true,
references: { model: "exercises", key: "id" },
},
exercise_eight: {
type: DataTypes.INTEGER(), allowNull: true,
references: { model: "exercises", key: "id" },
},
date: {
type: DataTypes.DATEONLY, allowNull: false,
validate: {
notNull: { msg: "You need to provide date !" }
}
}
},
{
timestamps: false,
freezeTableName: true,
})
Workout.associate = models => {
Workout.belongsToMany(models.exercises, {
foreignKey: "exercise_one",
through: 'exerciseOne',
targetKey: "id"
})
Workout.belongsToMany(models.exercises, {
foreignKey: "exercise_two",
through: 'exercisTwo',
targetKey: "id"
})
Workout.belongsToMany(models.exercises, {
foreignKey: "exercise_three",
through: 'exerciseThree',
targetKey: "id"
})
Workout.belongsToMany(models.exercises, {
foreignKey: "exercise_four",
through: 'exerciseFour',
targetKey: "id"
})
Workout.belongsToMany(models.exercises, {
foreignKey: "exercise_five",
through: 'exerciseFive',
targetKey: "id"
})
Workout.belongsToMany(models.exercises, {
foreignKey: "exercise_six",
through: 'exerciseSix',
targetKey: "id"
})
Workout.belongsToMany(models.exercises, {
foreignKey: "exercise_seven",
through: 'exerciseSeven',
targetKey: "id"
})
Workout.belongsToMany(models.exercises, {
foreignKey: "exercise_eight",
through: 'exerciseEight',
targetKey: "id"
})
Workout.belongsTo(models.workout_volume, {
foreignKey:"id",
targetKey: "workout_id",
through: "workout_volume"
})
}
Workout.sync()
return Workout
}
module.exports = workout
Workout_Volume.js
const workoutVolume = (sequelize, DataTypes) => {
const Workout_Volume = sequelize.define(
'workout_volume', {
workout_id: {
type: DataTypes.INTEGER, allowNull: false,
references: { model: "workouts", key: "id" },
validate: {
notNull: { msg: "You need to provide workout_id !" }
}
},
exercise_id: {
type: DataTypes.INTEGER, allowNull: false,
validate: {
notNull: { msg: "You need to provide exercise_id !" }
},
},
sets: {
type: DataTypes.INTEGER, allowNull: false,
validate: {
min: 1,
max: 100,
notNull: { msg: "You need to provide sets!" }
}
},
reps: {
type: DataTypes.INTEGER, allowNull: false,
validate: {
min: 1,
max: 100,
notNull: { msg: "You need to provide reps!" }
}
},
weight: {
type: DataTypes.INTEGER, allowNull: false,
validate: {
min: 0,
max: 1000,
notNull: { msg: "You need to provide sets!" }
}
}
},
{
timestamps: false,
freezeTableName: true,
},
)
Workout_Volume.associate = models => {
Workout_Volume.belongsTo(models.workouts,{
foreignKey: "workout_Id",
as: "workoutId",
targetKey: "id"
})
}
Workout_Volume.sync()
return Workout_Volume
}
module.exports = workoutVolume
My thoughts on making is:
To make associations with exercise_id to each exercise value in Workouts.js. The problem is that the exercises are 8 and I think the project will become more hard-coded;
Don't make assosiactions but make a for loop where I will check if the requested workout_id and exercises_id match the Workout.js values;
Or if there is better way to consturct the tables so it makes more sence
Or if there is better way to construct tables or something else.
workoutVolume.controller.js
const models = require('../models');
const Workout_Volume = models.workout_volume;
exports.getAllWorkouts_Volume = async (req, res) => {
try {
const workout_volume = await Workout_Volume.findAll()
return res.status(200).send(workout_volume)
} catch (error) {
return res.status(500).send(error)
}
}
exports.getWorkout_Volume = async (req, res) => {
try {
const workout_volume = await Workout_Volume.findByPk(req.params.id)
res.status(200).send(workout_volume)
} catch (error) {
return res.status(500).send(error)
}
}
exports.createWorkout_Volume = async (req, res) => {
try {
const exerciseExists = await Workout_Volume.findAll({
where: {
exercise_id: req.body.exercise_id,
workout_id: req.body.workout_id
},
attributes: ["exercise_id", "workout_id"]
})
if (exerciseExists.length !== 0) {
return res.status(500).send("Exercise already done for this workout!")
}
await Workout_Volume.create(req.body)
return res.status(201).send(req.body)
} catch (error) {
return res.status(500).send(error)
}
}
exports.updateWorkout_volume = async (req, res) => {
try {
const workout_volume = await Workout_Volume.findByPk(req.params.id)
console.log(req.body)
const exerciseExists = await Workout_Volume.findAll({
where: {
exercise_id: req.body.exercise_id,
workout_id: req.body.workout_id
},
attributes: ["exercise_id", "workout_id"]
})
if (exerciseExists.length !== 0) {
return res.status(500).send("Exercise already done for this workout!")
}
await workout_volume.update(req.body)
return res.status(200).send(req.body)
} catch (error) {
return res.status(500).send(error)
}
}
The problem here is that when I create I can't create invalid workout_id but can create exercise_id which is not in Workout.js.
Also when I try to update it already the length is !== 0
You definitely need to create a table/model Workout_Excercise instead of adding columns in Workout table/model. That way you will have as many exercises in a certain workout as you want.
And the second great benefit of having Workout_Excercise is that you will be able to create a correct foreign key from Workout_Volume to Workout_Excercise:
Workout.hasMany(models.workoutExercises, {
foreignKey: "workoutId",
})
WorkoutExercise.belongsTo(models.exercises, {
foreignKey: "exerciseId",
})
WorkoutExercise.hasMany(models.workout_volume, {
foreignKey: "workoutExerciseId",
})
WorkoutVolume.belongsTo(models.workout_exercise, {
foreignKey: "workoutExerciseId",
})

Sequelize belongTo association giving error "table name "contact" specified more than once"

I am trying to connect the phone model to the contact model through the contact_phone model. When I .findAll like below, the error returns: Unhandled rejection SequelizeDatabaseError: table name "contact" specified more than once. Are the associations/'as' correct in this case? The error happens when I try to include the contact_phone model. I have also inserted the table with an initial phone row.
exports.findAll = (req, res) => {
models.contact.findAll({
include: [
{
model: models.contact_phone,
as: 'contact'
}
]
})
.then(contacts => {
res.status(200).send(contacts);
});
};
Models
module.exports = (sequelize) => {
const Contact = sequelize.define(
'contact',
{
contact_id: {
type: Sequelize.BIGINT,
primaryKey: true,
autoIncrement: true,
field: 'contact_id'
},
first_name: {
type: Sequelize.STRING,
field: 'first_name'
},
last_name: {
type: Sequelize.STRING,
field: 'last_name',
},
created_at: {
type: Sequelize.DATE,
field: 'created_at'
},
updated_at: {
type: Sequelize.DATE,
field: 'updated_at'
}
},
{
tableName: 'contacts',
freezeTableName: true,
}
);
Contact.associate = (models) => {
Contact.belongsTo(models.contact_phone, {foreignKey: 'contact_id', as: 'contact'});
Contact.hasOne(models.phone, {foreignKey: 'phone_id', through: models.contact_phone});
};
return Contact;
};
module.exports = (sequelize) => {
const ContactPhone = sequelize.define(
'contact_phone',
{
contact_id: {
type: Sequelize.BIGINT,
references: {
model: 'contact',
key: 'contact_id'
}
},
phone_id: {
type: Sequelize.STRING,
references: {
model: 'phone',
key: 'phone_id'
}
}
}
);
ContactPhone.associate = (models) => {
ContactPhone.belongsTo(models.phone, {foreignKey: 'phone_id', as: 'phone'});
ContactPhone.belongsTo(models.contact, {foreignKey: 'contact_id', as: 'contact'});
};
return ContactPhone;
};
module.exports = (sequelize) => {
const Phone = sequelize.define(
'phone',
{
phone_id: {
type: Sequelize.STRING,
primaryKey: true,
field: 'phone_id'
},
updated_at: {
type: Sequelize.DATE,
field: 'updated_at'
},
created_at: {
type: Sequelize.DATE,
field: 'created_at'
}
},
{
tableName: 'phones',
freezeTableName: true,
}
);
Phone.associate = (models) => {
Phone.belongsTo(models.contact, {foreignKey: 'phone_id', through: models.contact_phone});
};
return Phone;
};
you can do like this . you don't have include extra contact_phone you can directly do with contact & phone with association like this .
contact module :
module.exports = (sequelize) => {
const Contact = sequelize.define(
'contact',
{
contact_id: {
type: Sequelize.INT,
primaryKey: true,
autoIncrement: true,
field: 'contact_id'
},
first_name: {
type: Sequelize.STRING,
field: 'first_name'
},
last_name: {
type: Sequelize.STRING,
field: 'last_name',
},
created_at: {
type: Sequelize.DATE,
field: 'created_at'
},
updated_at: {
type: Sequelize.DATE,
field: 'updated_at'
}
},
{
tableName: 'contacts',
freezeTableName: true,
}
);
Contact.associate = (models) => {
Contact.hasOne(models.phone, { // has many or hasOne relationship .here read like contact has one phone
foreignKey: 'phone_id',
onDelete: "CASCADE"
});
};
return Contact;
};
phone module :
module.exports = (sequelize) => {
const Phone = sequelize.define(
'phone',
{
phone_id: {
type: Sequelize.INT,
primaryKey: true,
autoIncrement: true,
field: 'phone_id'
},
contact_id: {
type: Sequelize.INT,
},
updated_at: {
type: Sequelize.DATE,
field: 'updated_at'
},
created_at: {
type: Sequelize.DATE,
field: 'created_at'
}
},
{
tableName: 'phones',
freezeTableName: true,
}
);
Phone.associate = (models) => {
Phone.belongsTo(models.contact, {
foreignKey: 'contact_id',
as: 'contact',
onDelete: "CASCADE"
});
};
}
your query be like :
models.contact.findAll({
include: [
{
model: models.phone,
as: 'contact'
}
]
}).then(contacts => {
res.status(200).send(contacts);
});
EDIT :
how can you define more association with same key and different alias
Phone.associate = (models) => {
Phone.belongsTo(models.contact, {
foreignKey: 'contact_id',
as: 'contact',
onDelete: "CASCADE"
});
Phone.belongsTo(models.contact, {
foreignKey: 'contact_id',
as: 'phone',
onDelete: "CASCADE"
});
};

Sequelize many-to-many relation and one-to-many in intermediate table

Hi have a table Users with many to many relation to Groups through UsersGroups. UsersGroups has a FK to UsersGroupsRoles:
Users:
module.exports = (sequelize, Sequelize) => {
const Users = sequelize.define(
'Users',
{
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true,
},
name: {
type: Sequelize.STRING,
},
},
{}
);
Users.associate = function(models) {
Users.belongsToMany(models.Groups, { through: models.UsersGroups });
};
return Users;
};
Groups:
module.exports = (sequelize, Sequelize) => {
const Groups = sequelize.define(
'Groups',
{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
name: {
type: Sequelize.STRING,
},
},
{}
);
Groups.associate = function(models) {
Groups.belongsToMany(models.Users, { through: models.UsersGroups });
};
return Groups;
};
UsersGroups:
module.exports = (sequelize, Sequelize) => {
const UsersGroups = sequelize.define(
'UsersGroups',
{
order: {
allowNull: true,
type: Sequelize.INTEGER,
defaultValue: 10000,
},
},
{}
);
UsersGroups.associate = function(models) {
UsersGroups.belongsTo(models.UsersGroupsRoles, { as: 'UsersGroupsRoles', onDelete: 'CASCADE' });
};
return UsersGroups;
};
UsersGroupsRoles:
module.exports = (sequelize, Sequelize) => {
const UsersGroupsRoles = sequelize.define(
'UsersGroupsRoles',
{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
role: {
type: Sequelize.STRING,
},
},
{}
);
UsersGroupsRoles.associate = function(models) {
UsersGroupsRoles.hasMany(models.UsersGroups, { as: 'UsersGroupsRoles', onDelete: 'CASCADE' });
};
return UsersGroupsRoles;
};
Now I want to query Users and UsersGroups, and get the UsersGroupsRoles:
models.Groups.findAll({
attributes: {
exclude: ['createdAt', 'updatedAt'],
},
include: [
{
model: models.Users,
attributes: {
exclude: ['createdAt', 'updatedAt', 'email', 'password'],
},
through: {
include: [
{
model: models.UsersGroupsRoles,
as: 'UsersGroupsRoles',
},
],
},
},
],
})
But the query fails with TypeError: Cannot read property 'indexOf' of undefined. I suppose it is because the include clause inside through, but then, what is the correct way to include a one-to-many association in an intermediate table?
Thanks!
What about try this?
try {
let groups = await models.Groups.findAll({
attributes: {
exclude: ['id','createdAt', 'updatedAt'],
}
})
const userGroupsPromises = groups.map(group => {
return models.UsersGroups.findAll({
where: {
groupId: group.id
},
include: [{
model: models.User,
attributes: {
exclude: ['createdAt', 'updatedAt', 'email', 'password'],
}
}, {
model: models.UsersGroupsRoles,
}]
})
})
const userGroupsPromiseResult = await Promise.all(userGroupsPromises)
groups = groups.map((group, index) => {
const _group = group.get()
_group.UserGroups = userGroupsPromiseResult[index]
return _group
})
console.log(groups)
} catch (err) {
console.log(err)
}

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