Related
I have two tables in a MySQL schema, 'members' and 'events', which share a many-to-many relationship. I am attempting to model that relationship using Sequelize via a junction table containing a member_id and an event_id (which correspond to columns in the Members and Events tables respectively) and an event_date - the date when an event is attended by many members.
I am following the guidance in the Sequelize 'Advanced Associations' section (link), but am getting an error when my node.js server attempts to start, as follows:
Members.belongsToMany(models.Events, { through: 'member_events' })
^
TypeError: Members.belongsToMany is not a function
I'm really stuggling to understand what specifically this means and how I can address the issue. The following is my code for the three models in question:
memberEvents.js
module.exports = (sequelize, DataTypes) => {
const Members = require('../models/members')
const Events = require('../models/events')
const MemberEvents = sequelize.define(
"MemberEvents",
{
member_id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
},
event_id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
},
event_date: {
type: DataTypes.DATEONLY,
allowNull: false,
primaryKey: true,
},
},
{ tableName: "member_events" }
);
Members.belongsToMany(Events, { through: 'member_events' })
Events.belongsToMany(Members, { through: 'member_events' })
return MemberEvents;
};
Members.js
module.exports = (sequelize, DataTypes) => {
//Below creates the member table in the schema
const Members = sequelize.define(
"Members",
{
member_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
forename: {
type: DataTypes.STRING(35),
allowNull: false,
},
surname: {
type: DataTypes.STRING(35),
allowNull: false,
},
date_of_birth: {
type: DataTypes.DATEONLY,
allowNull: false,
},
address_1: {
type: DataTypes.STRING(35),
allowNull: false,
},
address_2: {
type: DataTypes.STRING(35),
},
address_3: {
type: DataTypes.STRING(35),
},
address_4: {
type: DataTypes.STRING(35),
},
address_5: {
type: DataTypes.STRING(35),
},
postcode: {
type: DataTypes.STRING(12),
allowNull: false,
},
directions: {
type: DataTypes.TEXT("long"),
},
mobile_phone: {
type: DataTypes.STRING(12),
},
email_address: {
type: DataTypes.STRING(255),
},
key_safe_code: {
type: DataTypes.STRING(8),
},
next_of_kin_name: {
type: DataTypes.STRING(70),
},
next_of_kin_phone: {
type: DataTypes.STRING(12),
},
next_of_kin_local: {
type: DataTypes.STRING(33),
},
next_of_kin_relationship: {
type: DataTypes.STRING(40),
},
doctor_name: {
type: DataTypes.STRING(35),
},
initial_medical_conditions: {
type: DataTypes.TEXT("long"),
},
deceased: {
type: DataTypes.DATEONLY,
},
normally_escorted: {
type: DataTypes.STRING(3),
},
blue_badge_holder: {
type: DataTypes.STRING(3),
},
medical_equipment: {
type: DataTypes.STRING,
},
},
{ tableName: "Member" }
);
return Members;
};
Events.js
module.exports = (sequelize, DataTypes) => {
//Below creates the event table in the schema
const Events = sequelize.define(
"Events",
{
event_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
event_name: {
type: DataTypes.STRING(70),
allowNull: false,
},
staff_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{ tableName: "Events" }
);
return Events;
};
When separating your Sequelize models into separate imports, use the associate function to access the models to make these associations. You also don't need to import the other models into each other.
Something along these lines should work:
TableA.js
module.exports = (sequelize, DataTypes) => {
const TableA = sequelize.define('table_a', {
foobar: DataTypes.STRING,
}, {});
TableA.associate = function(models) {
TableA.belongsTo(models.TableB, { through: 'table_c' });
};
return TableA;
};
TableB.js
module.exports = (sequelize, DataTypes) => {
const TableB = sequelize.define('table_b', {
fazbaz: DataTypes.STRING,
}, {});
TableB.associate = function(models) {
TableB.belongsTo(models.TableA, { through: 'table_c' });
};
return TableB;
};
I'm trying to create a model in Sequelize with a hash password using bcrypt, and run "sequelize db:migrate" but I can't. I get this error whenever I try to run it. All I need to is have "password" with the hash already done. I don't know where I'm going wrong or what I'm missing. can anybody help me?
== 001-user: migrating =======
ERROR: s.replace is not a function
I'm using: Sequelize: 6.16.2. Database: Postgres. Node: 15. Bcrypt: 5.0.1
Here is my User model:
class User extends Model {
/**
* #param {import("../index")} models
*/
static associate (models) {
User.belongsTo(models.UserRole, { as: "user_role", foreignKey: "id_user_role" });
}
}
/**
* #param {import("sequelize").Sequelize} sequelize Connection with the database
*/
function initUser (sequelize) {
User.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
user: DataTypes.STRING(30),
name: DataTypes.STRING(100),
email: DataTypes.STRING(100),
password: DataTypes.STRING(100),
id_user_role: {
type: DataTypes.INTEGER,
references: {
model: sequelize.models.UserRole,
key: "id"
}
},
deleted: DataTypes.BOOLEAN
},
{
sequelize,
timestamps: false,
modelName: "User",
tableName: "user",
underscored: true,
hooks: {
beforeCreate: async (user) => {
if (user.password) {
const salt = await bcrypt.genSaltSync(11, "a");
user.password = bcrypt.hashSync(user.password, salt);
}
},
beforeUpdate: async (user) => {
if (user.password) {
const salt = await bcrypt.genSaltSync(11, "a");
user.password = bcrypt.hashSync(user.password, salt);
}
}
}
});
return User;
}
module.exports = { initUser };
User migration:
module.exports = {
/**
* #param {import("sequelize").QueryInterface} queryInterface
* #param {import("sequelize").DataTypes} Sequelize
*/
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable("user", {
id: {
type: Sequelize.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
user: {
type: Sequelize.STRING(30),
allowNull: false
},
name: {
type: Sequelize.STRING(100),
allowNull: false
},
email: {
type: Sequelize.STRING(100),
allowNull: false
},
password: {
type: Sequelize.STRING(100),
allowNull: false
},
id_user_role: {
type: Sequelize.INTEGER,
allowNull: false,
references: { model: UserRole, key: "id" }
},
deleted: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false
}
});
await queryInterface.bulkInsert("user", [
{
user: "admin", name: "test", email: "test#admin.com", password: "admin", id_user_role: 1
},
{
user: "comum", name: "test", email: "test#comum.com", password: "comum", id_user_role: 2
},
{
user: "regular", name: "test", email: "test#regular.com", password: "comum", id_user_role: 3
}
]);
}
};
This is my Diagram DATABASE : https://i.stack.imgur.com/CGAwh.png
I made models of my databases with SEQUELIZE like that :
MODEL : Level
module.exports = (sequelize, DataTypes) => {
const Level = sequelize.define(
'Level',
{
level_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
label: {
type: DataTypes.STRING,
allowNull: false,
unique: {
args: true,
msg: 'Level:Label already exist!',
},
validate: {
notEmpty: { msg: `Level:Label cannot be empty!` },
notNull: { msg: `Level:Label cannot be NULL!` },
},
},
ref: {
type: DataTypes.STRING,
allowNull: true,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
},
{
tableName: 'levels',
timestamps: false,
}
);
Level.associate = (models) => {
Level.belongsToMany(models.Test, {
through: models.testHasLevel,
foreignKey: 'level_id',
otherKey: 'test_id',
timestamps: false,
});
};
return Level;
};
Model : TEST :
module.exports = (sequelize, DataTypes) => {
const Test = sequelize.define(
'Test',
{
test_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
label: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: { msg: `Test:label cannot be empty!` },
notNull: { msg: `Test:label cannot be NULL!` },
},
},
isInternal: {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
validate: {
notEmpty: { msg: `Test:isInternal cannot be empty!` },
notNull: { msg: `Test:isInternal cannot be NULL!` },
},
},
parent_id: {
type: DataTypes.INTEGER,
defaultValue: null,
allowNull: true,
},
},
{
tableName: 'tests',
timestamps: false,
}
);
Test.associate = (models) => {
Test.belongsToMany(models.Level, {
through: models.testHasLevel,
foreignKey: 'test_id',
otherKey: 'level_id',
timestamps: false,
});
Test.hasMany(models.Test, { foreignKey: 'parent_id', as: 'children' });
};
return Test;
};
MODEL : TEST HAS MODEL
module.exports = (sequelize, DataTypes) => {
const testHasLevel = sequelize.define(
'testHasLevel',
{},
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
tableName: 'test_has_level',
timestamps: false,
}
);
testHasLevel.associate = (models) => {
testHasLevel.belongsTo(models.Test, {
foreignKey: 'test_id',
targetKey: 'test_id',
});
testHasLevel.belongsTo(models.Level, {
foreignKey: 'level_id',
targetKey: 'level_id',
});
};
return testHasLevel;
};
I made also SESSION MODEL :
module.exports = (sequelize, DataTypes) => {
const Session = sequelize.define(
'Session',
{
session_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
institut_id: {
type: DataTypes.INTEGER,
},
start: {
type: DataTypes.DATE,
},
end: {
type: DataTypes.DATE,
},
test_id: {
type: DataTypes.INTEGER,
},
level_id: {
type: DataTypes.INTEGER,
},
limitDateSubscribe: {
type: DataTypes.DATE,
},
placeAvailable: {
type: DataTypes.INTEGER,
},
},
{
tableName: 'sessions',
timestamps: false,
}
);
Session.associate = (models) => {
Session.hasMany(models.sessionHasUser, { foreignKey: 'session_id' });
};
return Session;
};
But i have no idea how to "BIND" SESSION with TEST_HAS_LEVEL with Sequelize ....
What should i change ? cause i know "composite key" are not allowed with the last version of sequelize.
In other term :
How associate properly a cross table with a one to many relationship to an other table ?
Model: Level
module.exports = (sequelize, DataTypes) => {
const Level = sequelize.define(
"Level",
{
level_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
label: {
type: DataTypes.STRING,
allowNull: false,
unique: {
args: true,
msg: "Level:Label already exist!",
},
validate: {
notEmpty: { msg: `Level:Label cannot be empty!` },
notNull: { msg: `Level:Label cannot be NULL!` },
},
},
ref: {
type: DataTypes.STRING,
allowNull: true,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
},
{
tableName: "levels",
timestamps: false,
}
);
Level.associate = (models) => {
Level.hasMany(models.testHasLevel, {
foreignKey: "level_level_id",
as: "levels",
});
};
return Level;
};
Model: Test
module.exports = (sequelize, DataTypes) => {
const Test = sequelize.define(
"Test",
{
test_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
label: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: { msg: `Test:label cannot be empty!` },
notNull: { msg: `Test:label cannot be NULL!` },
},
},
isInternal: {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
validate: {
notEmpty: { msg: `Test:isInternal cannot be empty!` },
notNull: { msg: `Test:isInternal cannot be NULL!` },
},
},
parent_id: {
type: DataTypes.INTEGER,
defaultValue: null,
allowNull: true,
},
},
{
tableName: "tests",
timestamps: false,
}
);
Test.associate = (models) => {
Test.hasMany(models.testHasLevel, {
foreignKey: "test_test_id",
as: "tests",
});
Test.hasMany(models.Test, { foreignKey: "parent_id", as: "children" });
};
return Test;
};
Model: Test has level
module.exports = (sequelize, DataTypes) => {
const testHasLevel = sequelize.define(
"testHasLevel",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
test_test_id: {
type: DataTypes.INTEGER,
},
level_level_id: {
type: DataTypes.INTEGER,
},
},
{
tableName: "test_has_level",
timestamps: false,
}
);
testHasLevel.associate = (models) => {
testHasLevel.belongsTo(models.Test, {
foreignKey: "test_test_id",
as: "tests",
});
testHasLevel.belongsTo(models.Level, {
foreignKey: "level_level_id",
as: "levels",
});
testHasLevel.hasMany(models.Session, {
foreignKey: "test_has_level_id",
as: "test_has_level",
});
};
return testHasLevel;
};
Model: Session
module.exports = (sequelize, DataTypes) => {
const Session = sequelize.define(
"Session",
{
session_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
institut_id: {
type: DataTypes.INTEGER,
},
start: {
type: DataTypes.DATE,
},
end: {
type: DataTypes.DATE,
},
test_has_level_id: {
type: DataTypes.INTEGER,
},
limitDateSubscribe: {
type: DataTypes.DATE,
},
placeAvailable: {
type: DataTypes.INTEGER,
},
},
{
tableName: "sessions",
timestamps: false,
}
);
Session.associate = (models) => {
Session.belongsTo(models.testHasLevel, {
foreignKey: "test_has_level_id",
as: "test_has_level",
});
};
return Session;
};
I’m new with sequelize I’m trying to make a request with associate tables
I have a first model called Experience
module.exports = function (sequelize, DataTypes) {
const Experience = sequelize.define('experience', {
internalId: {
type: DataTypes.BIGINT,
unique: true,
allowNull: false,
},
label: {
type: DataTypes.STRING,
unique: false,
allowNull: false,
},
picture: {
type: DataTypes.TEXT,
unique: false,
allowNull: true,
},
type: {
type: DataTypes.STRING,
validate: {
isIn: {
args: [[
'generic',
'specific',
]],
msg: 'Must be a valid type',
},
},
unique: false,
allowNull: true,
},
author: {
type: DataTypes.STRING,
unique: false,
allowNull: true,
defaultValue: 'import',
},
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
});
Experience.associate = (models) => {
Experience.belongsToMany(models.Tag, {
as: 'Tags',
through: models.ExperienceTag,
});
};
return Experience;
};
a second called Tags
module.exports = function (sequelize, DataTypes) {
const Tag = sequelize.define('tag', {
internalId: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
},
name: {
type: DataTypes.STRING,
unique: false,
allowNull: false,
},
author: {
type: DataTypes.STRING,
unique: false,
allowNull: true,
defaultValue: 'import',
},
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
});
Tag.associate = (models) => {
Tag.belongsToMany(models.Experience, {
as: 'Experiences',
through: models.ExperienceTag,
});
};
return Tag;
};
The association table name was ExperienceTags
I would like get all the Experiencewho have a tagId = 44
This is my request:
Experience.findAll({
include: [{
model: ExperienceTag,
where: { tagId: 44 },
}],
})
.then((results) => {
winston.warn(JSON.stringify(results, null, 2));
res.status(200)
.send(results);
})
.catch(error => res.status(500)
.send({ error: error.toString() }));
But when I execute it I have an error like:
{
"error": "SequelizeEagerLoadingError: experienceTag is not associated to experience!"
}
I think you like to include Tag rather than ExperienceTag, the following example may help you
Experience.findAll({
include: [{
model: Tag, //model name which you want to include
as: 'Tags', // you have to pass alias as you used while defining
where: { tagId: 44 },
}],
})
I think , you need to add as: 'Experiences' , in your include as you have defined association with alias
Change this
Experience.findAll({
include: [{
model: ExperienceTag,
where: { tagId: 44 },
}],
})
With
Experience.findAll({
include: [{
model: ExperienceTag,
as: 'Experiences', // <---- HERE
where: { tagId: 44 },
}],
})
I have some issue with getting full data from db.
That are my models:
User
module.exports = function(sequelize, DataTypes) {
return sequelize.define('user', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'ID'
},
password: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'password'
},
email: {
type: DataTypes.STRING(255),
allowNull: false,
unique: true,
field: 'email'
},
roleId: {
type: DataTypes.INTEGER(11),
allowNull: false,
references: {
model: 'role',
key: 'ID'
},
field: 'role_id'
}
}, {
timestamps: false,
tableName: 'user'
});
};
Role
module.exports = function(sequelize, DataTypes) {
return sequelize.define('role', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'ID'
},
name: {
type: DataTypes.STRING(255),
allowNull: false,
unique: true,
field: 'name'
},
description: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'description'
},
permission: {
type: DataTypes.INTEGER(11),
allowNull: false,
field: 'permission'
}
}, {
timestamps: false,
tableName: 'role',
});};
I want to get object of one specific user including all role content.
Somethink like
{
id: 4,
password: 'xxx',
email: 'adsads#saas.com',
role: {
id: 2,
name: 'admin'
description: 'ipsum ssaffa',
permission: 30
}
}
So I'm using:
User.findOne( { where: { id: req.userId }, include: [ Role ] } ).then( user =>{...});
but I get in the result err.message: "role is not associated to user"
And the simple question - what's wrong ? :)
*to handle models I'm using sequelize-cli
You get this error because you didn't add associate between the models
base on your json I see that each user only has one role, so you can either use belongsTo in role model or hasOne in user model
Should be something like this:
User.js
module.exports = function(sequelize, DataTypes) {
var user = sequelize.define('user', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'ID'
},
password: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'password'
},
email: {
type: DataTypes.STRING(255),
allowNull: false,
unique: true,
field: 'email'
},
roleId: {
type: DataTypes.INTEGER(11),
allowNull: false,
references: {
model: 'role',
key: 'ID'
},
field: 'role_id'
}
}, {
timestamps: false,
tableName: 'user'
});
user.associate = function(models) {
user.hasOne(models.role, {foreignKey: 'id',sourceKey: 'roleId'});
}
return user;
};
Role.js
module.exports = function(sequelize, DataTypes) {
var role = sequelize.define('role', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'ID'
},
name: {
type: DataTypes.STRING(255),
allowNull: false,
unique: true,
field: 'name'
},
description: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'description'
},
permission: {
type: DataTypes.INTEGER(11),
allowNull: false,
field: 'permission'
}
}, {
timestamps: false,
tableName: 'role',
});
role.associate = function(models) {
user.belongsTo(models.role, {foreignKey: 'id'});
}
return role;
};
You have to declare associations between your Models. If using Sequelize CLI make sure the static method associate is being called. Example:
/models.index.js
const Category = require('./Category');
const Product = require('./Product');
const ProductTag = require('./ProductTag');
const Tag = require('./Tag');
Category.associate({Product});
Product.associate({Category,Tag});
Tag.associate({Product});
module.exports={Category,Product,ProductTag,Tag};
and then the association in Category.js
'use strict';
const {Model,DataTypes} = require('sequelize');
const sequelize = require('../config/connection.js');
class Category extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method.
*/
static associate({Product}) {
// define association here
console.log('Category associated with: Product');
this.hasMany(Product, {
foreignKey: 'category_id',
onDelete: 'CASCADE'
});
}
}
Category.init({
category_id: {type: DataTypes.INTEGER, autoIncrement: true, allowNull: false, primaryKey: true},
category_name: {type: DataTypes.STRING, allowNull: false}
}, {
sequelize,
timestamps: false,
freezeTableName: true,
underscored: true,
modelName: "Category",
});
module.exports = Category;