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.
Related
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.
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 tried to create database with 3 tables: restaurant,restaurant_menu,menu and their relationship is restaurant have many menu and menu can belong to many restarant by sequelize in Nodejs.
restaurant.model.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const restaurant = sequelize.define('restaurant', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
name: DataTypes.STRING,
address: DataTypes.STRING,
phone: DataTypes.STRING,
lat: {
type: DataTypes.DOUBLE,
allowNull: false,
defaultValue: 0
},
lng: {
type: DataTypes.DOUBLE,
allowNull: false,
defaultValue: 0
},
user_owner: {
type: DataTypes.INTEGER,
defaultValue: 0
},
image: {
type: DataTypes.TEXT,
allowNull: false
},
payment_url: {
type: DataTypes.TEXT,
allowNull: false
}
}, {
freezeTableName: true,
timestamps: false
});
restaurant.associate = function (models) {
// associations can be defined here
restaurant.belongsToMany(models.menu, {
through: {
model: models.restaurant_menu
},
foreignKey: 'restaurant_id'
})
};
return restaurant;
};
menu.model.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const menu = sequelize.define('menu', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false
},
name: DataTypes.STRING(50),
description: DataTypes.STRING(500),
image: DataTypes.TEXT
}, {
freezeTableName: true,
timestamps: false
});
menu.associate = function (models) {
// associations can be defined here
menu.belongsToMany(models.restaurant, {
through: {
model: models.restaurant_menu
},
foreignKey: "menu_id"
});
};
return menu;
};
restaurant_menu.model.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const restarant_menu = sequelize.define('restarant_menu', {
restaurant_id: {
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
references: {
model: 'restaurant'
}
},
menu_id: {
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
references: {
model: 'menu'
}
}
}, {
freezeTableName: true,
timestamps: false
});
restarant_menu.associate = function (models) {
// associations can be defined here
};
return restarant_menu;
};
i tried to run migration, but i get error:
Cannot read property 'menu_id' of undefined
How can I fix it?
I believe you are writing old syntax, checkout documentation.
https://sequelize.org/master/manual/advanced-many-to-many.html
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)