Good evenings,fine sirs. I am working with Sequelize with NodeJS. I created Many-to-many association between two table User and Project.
//This is Project Model
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Project extends Model {
static associate(models) {
const { User, Issue } = models;
this.belongsToMany(User, {
as: "project_users",
foreignKey: "project_id",
through: "projectuser",
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
this.hasMany(Issue, {
as: "project_issues",
foreignKey: "project_id",
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
}
}
Project.init(
{
project_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
projectname: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.STRING,
},
projecturl: {
type: DataTypes.STRING,
},
},
{
sequelize,
modelName: "Project",
}
);
return Project;
};
//This is User Model
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class User extends Model {
static associate(models) {
const { Commet, Project, Issue } = models;
this.hasMany(Commet, {
foreignKey: "user_id",
as: "user_commets",
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
this.belongsToMany(Project, {
as: "project_user",
foreignKey: "user_id",
through: "projectuser",
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
this.belongsToMany(Issue, {
as: "user_issues",
foreignKey: "user_id",
through: "issueuser",
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
}
}
User.init(
{
user_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
role: {
type: DataTypes.STRING,
allowNull: false,
validate: {
roleValidator(role) {
let rolet = role.toLowerCase();
if (roles[rolet] === undefined) {
throw new Error("Invalid Role");
}
},
},
},
username: { type: DataTypes.STRING, allowNull: false },
usersurname: { type: DataTypes.STRING, allowNull: false },
avatarURL: { type: DataTypes.STRING, allowNull: true },
password: {
type: DataTypes.STRING,
validate: {
passwordValidator(password) {
if (password.length <= 5) {
throw new Error("Cannot be less than 5 characters");
}
},
},
},
email: {
type: DataTypes.STRING,
unique: true,
validate: { isEmail: true },
},
},
{
sequelize,
modelName: "User",
}
);
return User;
};
My question may be silly but, do i need to create junction table manually to access it from my code or is there any way to access it without creating it? Thank you for your answers.
Related
I'm trying to make a save that corresponds to three models: Person ->(hasOne)-> ContactInfo ->(hasMany)-> Phone.
Models:
Person:
'use strict';
const chalk = require('chalk');
const { Model } = require('sequelize');
const { gender } = require("../utils/enumList");
const { ageCalculation } = require('../utils/extraFunctions');
module.exports = (sequelize, DataTypes) =>
{
class Person extends Model
{
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models)
{
//Has one
Person.hasOne(models.ContactInfo, {
as: 'contactInfo_R',
foreignKey: {
name: "personId",
allowNull: false,
type: DataTypes.UUID,
unique: true,
},
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
Person.hasOne(models.Holder, {
as: 'holder_R',
foreignKey: {
name: "personId",
type: DataTypes.UUID,
allowNull: false,
unique: true,
},
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
Person.hasOne(models.Intermediary, {
as: 'intermediary_R',
foreignKey: {
name: "personId",
allowNull: false,
type: DataTypes.UUID,
unique: true,
},
onDelete: "CASCADE",
onUpdate: "CASCADE"
});
//Has many
Person.hasMany(models.Insured, {
as: "insured_R",
foreignKey: {
name: "personId",
allowNull: false,
type: DataTypes.UUID,
unique: false,
},
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
}
}
Person.init({
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV1,
primaryKey: true,
},
names: {
type: DataTypes.STRING,
allowNull: false,
},
surnames: {
type: DataTypes.STRING,
allowNull: false,
},
birth_date: {
type: DataTypes.DATEONLY,
allowNull: false,
},
age: {
type: DataTypes.VIRTUAL,
get()
{
return ageCalculation(this.getDataValue("birth_date"));
},
set(value)
{
throw new Error("No es necesario introducir la edad de la persona");
}
},
gender: {
type: DataTypes.ENUM(gender),
allowNull: false,
},
dni: {
type: DataTypes.STRING,
allowNull: true,
},
support_dni_address: {
type: DataTypes.STRING,
allowNull: true,
}
}, {
sequelize,
modelName: 'Person',
});
return Person;
};
ContactInfo:
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) =>
{
class ContactInfo extends Model
{
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models)
{
// define association here
//Has many
ContactInfo.hasMany(models.Phone, {
as: "phone_R",
foreignKey: {
name: "contactInfoId",
type: DataTypes.UUID,
allowNull: false,
},
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
//Belong to
ContactInfo.belongsTo(models.Person, {
as: "person_R",
foreignKey: {
name: "personId",
type: DataTypes.UUID,
allowNull: false,
unique: true,
},
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
}
}
ContactInfo.init({
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV1,
primaryKey: true,
},
nation: {
type: DataTypes.STRING,
allowNull: false,
},
region: {
type: DataTypes.STRING,
allowNull: true,
},
city: {
type: DataTypes.STRING,
allowNull: true
},
address_1: {
type: DataTypes.STRING,
allowNull: true,
},
address_2: {
type: DataTypes.STRING,
allowNull: true,
},
email: {
type: DataTypes.STRING,
allowNull: false,
validate: {
isEmail: {
arg: true,
msg: "Por favor, usar un formato de correo electrónico valido"
},
},
},
}, {
sequelize,
modelName: 'ContactInfo',
});
return ContactInfo;
};
Phone:
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) =>
{
class Phone extends Model
{
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models)
{
//Belong to
Phone.contactInfo = Phone.belongsTo(models.ContactInfo, {
as: "contactInfo_R",
foreignKey: {
name: "contactInfoId",
allowNull: false,
type: DataTypes.UUID,
unique: false
},
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
}
}
Phone.init({
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV1,
primaryKey: true,
},
nation_phone_code: {
type: DataTypes.STRING,
allowNull: false,
//TODO add predefined list
},
phone_number: {
type: DataTypes.STRING,
allowNull: false,
//TODO add formatting and validating
}
}, {
sequelize,
modelName: 'Phone',
});
return Phone;
};
Build Code:
let HolderPerson = await Models.Person.build({
names: personHolder_form.names,
surnames: personHolder_form.surnames,
birth_date: personHolder_form.birth_date,
gender: personHolder_form.gender,
dni: personHolder_form.dni,
support_dni_address: personHolder_form.support_dni_address,
contactInfo_R: {
nation: ContactInfoHolder_form.nation,
region: ContactInfoHolder_form.region,
city: ContactInfoHolder_form.city,
address_1: ContactInfoHolder_form.address_1,
address_2: ContactInfoHolder_form.address_2,
email: ContactInfoHolder_form.email,
phone_R: [
{ phone_number: '04269872654', nation_phone_code: '+58' },
{ phone_number: '02569871452', nation_phone_code: '+57' }
],
}
}, {
include: [{
association: {
model: Models.ContactInfo,
as: 'contactInfo_R'
},
include: [{
model: Models.Phone,
as: 'phone_R'
}]
}],
});
console.log(HolderPerson);
Console Error:
TypeError: Cannot read property 'name' of undefined
at Function._conformInclude (/home/sistemas/proyectos/Sistema_PreCris/node_modules/sequelize/lib/model.js:301:50)
at /home/sistemas/proyectos/Sistema_PreCris/node_modules/sequelize/lib/model.js:270:61
at Array.map (<anonymous>)
at Function._conformIncludes (/home/sistemas/proyectos/Sistema_PreCris/node_modules/sequelize/lib/model.js:270:39)
at new Model (/home/sistemas/proyectos/Sistema_PreCris/node_modules/sequelize/lib/model.js:104:24)
at new Person (/home/sistemas/proyectos/Sistema_PreCris/src/models/Person.js:10:5)
at Function.build (/home/sistemas/proyectos/Sistema_PreCris/node_modules/sequelize/lib/model.js:1326:12)
at primeraParte (/home/sistemas/proyectos/Sistema_PreCris/src/controllers/pruebas.js:86:52)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
I've done what I understand from the sequelize documentation but I can't get it to work. If I remove the association from the third model it works. I do not know what else to do.
sirs. I have 2 models in sequelize ,Project and User. Relationship between this two models is many-to-many. Fot this relation, I have created projectuser. But when i trying to insert intoprojectuser console gives me ReferenceError: Cannot access 'projectuser' before initialization error. [NodeJs][Sequelize] ReferenceError: Cannot access 'ModelName' before initialization I have tried this solution but i think it is too complex solution. Can you please help me?
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Project extends Model {
static associate(models) {
const { User, Issue, projectuser } = models;
this.belongsToMany(User, {
as: "project_users",
foreignKey: "project_id",
through: "projectuser",
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
this.hasMany(Issue, {
as: "project_issues",
foreignKey: "project_id",
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
}
}
Project.init(
{
project_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
projectname: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
},
projecturl: {
type: DataTypes.STRING,
},
},
{
sequelize,
modelName: "Project",
}
);
return Project;
};
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class User extends Model {
static associate(models) {
const { Commet, Project, Issue, projectuser, issueuser } = models;
this.hasMany(Commet, {
foreignKey: "user_id",
as: "user_commets",
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
this.belongsToMany(Project, {
as: "project_user",
foreignKey: "user_id",
through: "projectuser",
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
this.belongsToMany(Issue, {
as: "user_issues",
foreignKey: "user_id",
through: issueuser,
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
}
}
User.init(
{
user_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
role: {
type: DataTypes.STRING,
allowNull: false,
validate: {
roleValidator(role) {
let rolet = role.toLowerCase();
if (roles[rolet] === undefined) {
throw new Error("Invalid Role");
}
},
},
},
username: { type: DataTypes.STRING, allowNull: false },
usersurname: { type: DataTypes.STRING, allowNull: false },
avatarURL: { type: DataTypes.STRING, allowNull: true },
password: {
type: DataTypes.STRING,
validate: {
passwordValidator(password) {
if (password.length <= 5) {
throw new Error("Cannot be less than 5 characters");
}
},
},
},
email: {
type: DataTypes.STRING,
unique: true,
validate: { isEmail: true },
},
},
{
sequelize,
modelName: "User",
}
);
return User;
};
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class projectuser extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
projectuser.init({
deneme: DataTypes.STRING
}, {
sequelize,
modelName: 'projectuser',
});
return projectuser;
};
When I try to insert new category, I got this error:
error: column "image" does not exist
sql: 'INSERT INTO "Categories" ("id","createdAt","updatedAt") VALUES (DEFAULT,$1,$2) RETURNING "id","image","title","createdAt","updatedAt";'
The problem is that it doesn't insert name and other values and returns columns belong to post table.
My guesses are the problem of sequelize-cli and sequelize version or missing something in models or migrations.
I only insert values into name, createdAt and updatedAt column:
await Category.create({
name: req.body.name,
createdAt: new Date(),
updatedAt: new Date()
});
My category model:
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Category extends Model {
static associate(models) {
Category.hasMany(models.Post, { as: "posts", foreignKey: "categoryId" });
}
}
Category.init(
{
name: DataTypes.STRING
},
{
sequelize,
modelName: "Category"
}
);
return Category;
};
My Post Model:
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Post extends Model {
static associate(models) {
Post.belongsTo(models.Category, { foreignKey: "categoryId", onDelete: "CASCADE", as: "category" });
}
}
Post.init(
{
title: DataTypes.STRING,
image: DataTypes.STRING,
content: DataTypes.TEXT,
categoryId: DataTypes.INTEGER
},
{
sequelize,
modelName: "Post"
}
);
return Post;
};
Post migration:
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("Posts", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
title: {
type: Sequelize.STRING
},
image: {
type: Sequelize.STRING
},
content: {
type: Sequelize.TEXT
},
categoryId: {
type: Sequelize.INTEGER,
allowNull: false,
onDelete: "CASCADE",
references: {
model: "Categories",
key: "id"
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
Category migration:
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("Categories", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
I couldn't find solution for this, therefor I used sequelize.query
I would like to find out what is the right way to add a many-to-many relation when seeding records with sequelize-cli. Now I understand that the easiest way to do this is to create another seeder file and manually set the values but is there a way to make some changes to my 2_user seeder file so that when a user is seeded with some value given for the role it automatically makes a record in the user_roles table. So basically same as the user.setRoles() one can use but in the seeder files. Any help is much appreciated.
Models
user.model.js
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
static associate(models) {
User.belongsToMany(models.Role, {
through: 'user_roles',
foreignKey: 'user_id',
otherKey: 'role_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
User.hasMany(models.User_Role);
User.hasMany(models.Address, {
foreignKey: 'user_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
}
}
User.init(
{
user_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
email: DataTypes.STRING,
password: DataTypes.STRING,
bio: DataTypes.STRING,
activity_status: DataTypes.BOOLEAN,
},
{
sequelize,
modelName: 'User',
}
);
return User;
};
role.model.js
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
static associate(models) {
User.belongsToMany(models.Role, {
through: 'user_roles',
foreignKey: 'user_id',
otherKey: 'role_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
User.hasMany(models.User_Role);
User.hasMany(models.Address, {
foreignKey: 'user_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
}
}
User.init(
{
user_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
email: DataTypes.STRING,
password: DataTypes.STRING,
bio: DataTypes.STRING,
activity_status: DataTypes.BOOLEAN,
},
{
sequelize,
modelName: 'User',
}
);
return User;
};
user_role.model
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User_Role extends Model {
static associate(models) {
User_Role.belongsTo(models.User, {
foreignKey: 'user_id',
});
User_Role.belongsTo(models.Role, {
foreignKey: 'role_id',
});
}
}
User_Role.init(
{
user_role_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
},
{
sequelize,
modelName: 'User_Role',
}
);
return User_Role;
};
Migrations
1-create-user
'use strict';
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('users', {
user_id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
email: {
type: Sequelize.STRING,
unique: true,
},
password: {
type: Sequelize.STRING,
},
bio: {
type: Sequelize.STRING,
},
activity_status: {
type: Sequelize.BOOLEAN,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('users');
},
};
3-create-role
'use strict';
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('roles', {
role_id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
unique: true,
type: Sequelize.INTEGER,
},
user_type: {
type: Sequelize.STRING,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('roles');
},
};
6-create-user_role
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('user_roles', {
user_role_id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
user_id: {
type: Sequelize.INTEGER,
references: { model: 'users', key: 'user_id' },
onDelete: 'CASCADE',
},
role_id: {
type: Sequelize.INTEGER,
references: { model: 'roles', key: 'role_id' },
onDelete: 'CASCADE',
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('user_roles');
},
};
Seeders
1_role
'use strict';
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.bulkInsert(
'roles',
[
{
user_type: 'courier',
createdAt: new Date(),
updatedAt: new Date(),
},
{
user_type: 'receiver',
createdAt: new Date(),
updatedAt: new Date(),
},
{
user_type: 'donor',
createdAt: new Date(),
updatedAt: new Date(),
},
{
user_type: 'admin',
createdAt: new Date(),
updatedAt: new Date(),
},
],
{}
);
},
async down(queryInterface, Sequelize) {
await queryInterface.bulkDelete('Roles', null, {});
},
};
2_user
('use strict');
var bcrypt = require('bcryptjs');
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.bulkInsert(
'users',
[
{
email: 'courier#email.com',
password: bcrypt.hashSync('PassWord123#', 10),
bio: 'This is a courier type user!',
activity_status: true,
createdAt: new Date(),
updatedAt: new Date(),
role_id: 1,
},
{
email: 'donor#email.com',
password: bcrypt.hashSync('PassWord123#', 10),
bio: 'This is a donor type user!',
activity_status: true,
createdAt: new Date(),
updatedAt: new Date(),
},
{
email: 'receiver#email.com',
password: bcrypt.hashSync('PassWord123#', 10),
bio: 'This is a Receiver type user!',
activity_status: true,
createdAt: new Date(),
updatedAt: new Date(),
},
{
email: 'admin#email.com',
password: bcrypt.hashSync('PassWord123#', 10),
bio: 'This is a Admin type user!',
activity_status: true,
createdAt: new Date(),
updatedAt: new Date(),
},
],
{}
);
},
async down(queryInterface, Sequelize) {
await queryInterface.bulkDelete('Users', null, {});
},
};
I made some associate but it did not work, probably with me that something is wrong, ask for help.
There are two models
module.exports = function (sequelize, DataTypes) {
var pages_lang = require('./pages_lang')(sequelize, DataTypes);
return sequelize.define('pages', {
id: {
type: DataTypes.INTEGER(10),
allowNull: false,
primaryKey: true,
autoIncrement: true,
references : { model: "pages_lang", key: "page_id" }
},
name: {
type: DataTypes.STRING,
allowNull: false
},
published: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: '0'
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: '0000-00-00 00:00:00'
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: '0000-00-00 00:00:00'
}
}, {
classMethods: {
associate: function (models) {
this.hasMany(models.pages_lang, {onDelete: 'SET NULL', onUpdate: 'CASCADE', foreignKey: 'page_id', as: 'pages', through: models.pages_lang});
},
getAll() {
return this.findAll({include: [{model: pages_lang, as: 'pages_lang'}]}).then(function (result) {
return result;
});
}
}
});
};
module.exports = function (sequelize, DataTypes) {
return sequelize.define('pages_lang', {
id: {
type: DataTypes.INTEGER(10),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
page_id: {
type: DataTypes.INTEGER(10),
allowNull: false,
references : { model: "pages", key: "id" }
},
content: {
type: DataTypes.TEXT,
allowNull: false
}
}, {
classMethods: {
associate: function (models) {
this.belongsTo(models.pages, {foreignKey: 'id', foreignKeyConstraint:true, as: 'pages', through: models.pages});
}
}
});
};
But when you call results in an error
Unhandled rejection Error: pages_lang (pages_lang) is not associated
to pages!
Advance very grateful for the help
Your association alias (as) should match what you pass to findAll
this.hasMany(models.pages_lang, {onDelete: 'SET NULL', onUpdate: 'CASCADE', foreignKey: 'page_id', as: 'pages_lang' });
return this.findAll({include: [{model: pages_lang, as: 'pages_lang'}]});
Since the model is already called pages_lang, you can also skip the alias completely:
this.hasMany(models.pages_lang, {onDelete: 'SET NULL', onUpdate: 'CASCADE', foreignKey: 'page_id'});
return this.findAll({include: [pages_lang]});
Notice that I removed the through argument - it should only be used for belongsToMany (many-to-many)