I'm totally newbie with Sequelize and I'm getting the next error when I run my application and sync my tables:
TypeError: UserModel.hasMany is not a function
at Object.<anonymous> (/home/josecarlos/Workspace/nodejs/remote-roofing/src/server/models/users.js:46:11)
at Module._compile (internal/modules/cjs/loader.js:816:30)
at Module._compile (/home/josecarlos/Workspace/nodejs/remote-roofing/node_modules/pirates/lib/index.js:99:24)
at Module._extensions..js (internal/modules/cjs/loader.js:827:10)
at Object.newLoader [as .js] (/home/josecarlos/Workspace/nodejs/remote-roofing/node_modules/pirates/lib/index.js:104:7)
I've got two tables only, so I have defined two models. One for each one and try to relationship between them.
user.js
import ProjectModel from "./projects";
const UserModel = (sequelize, type) => {
return sequelize.define("user", {
id: {
type: type.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
email: {
type: type.STRING,
allowNull: false,
isEmail: {
msg: "The format of the e-mail is not correct"
},
validate: {
notNull: {
msg: "E-mail cannot be empty"
}
}
},
name: {
type: type.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Name cannot be empty"
}
}
},
surname: {
type: type.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Surname cannot be empty"
}
}
}
})
};
UserModel.hasMany(ProjectModel, { foreingKey: "userID", sourceKey: "id"});
ProjectModel.belongsTo(UserModel, { foreingKey: "userID", sourceKey: "id"});
module.exports.UserModel = UserModel;
project.js
import UserModel from "./users";
const ProjectModel = (sequelize, type) => {
return sequelize.define("project", {
id: {
type: type.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
name: {
type: type.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Name cannot be empty"
}
}
},
body: {
type: type.TEXT,
allowNull: false,
validate: {
notNull: {
msg: "Body cannot be empty"
}
}
},
status: {
type: type.ENUM("active", "inactive", "declined", "completed"),
allowNull: false,
validate: {
notNull: {
msg: "Status cannot be empty"
}
}
},
userID: {
type: type.INTEGER,
allowNull: false,
validate: {
notNull: {
msg: "userID cannot be empty"
}
}
}
})
};
module.exports.ProjectModel = ProjectModel;
This two models are create from db.js:
import Sequelize from "sequelize";
import UserModel from "./models/users";
import ProjectModel from "./models/projects";
//It's mandatory to import dotenv in each file where we can use enviroment variables
import config from "dotenv";
config.config();
const sequelize = new Sequelize(process.env.DDBB_NAME, process.env.DDBB_USER,process.env.DDBB_PSWD, {
host: process.env.DDBB_HOST,
port: process.env.DDBB_PORT,
define: {
//freezeTableName: true, /**Don't add 's to the end of each table/model */
//timestamps: false, /**Don't add fields createdAt and updatedAt */
},
dialect: "postgres",
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
}
);
const User = UserModel(sequelize, Sequelize);
const Project = ProjectModel(sequelize, Sequelize);
sequelize.sync({force: false}).then(() => {
console.log("Tables syncronized!!!")
})
module.exports = {
User,
Project
};
This file is required when I run the application. And I've got the error that I have showned before.
What am I doing wrong?
Edit I:
I have modified db.js file and it doesn't work to me. I've got the same error. I don't know how been called the models :(((((
import Sequelize from "sequelize";
import UserModel from "./models/users";
import ProjectModel from "./models/projects";
import fs from "fs";
import path from "path";
//It's mandatory to import dotenv in each file where we can use enviroment variables
import config from "dotenv";
config.config();
const sequelize = new Sequelize(process.env.DDBB_NAME, process.env.DDBB_USER,process.env.DDBB_PSWD, {
host: process.env.DDBB_HOST,
port: process.env.DDBB_PORT,
define: {
//freezeTableName: true, /**Don't add 's to the end of each table/model */
//timestamps: false, /**Don't add fields createdAt and updatedAt */
},
dialect: "postgres",
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
}
);
let db = {
sequelize,
Sequelize,
models: {}
}
//Register Models
const models = path.join(__dirname, "models");
fs
.readdirSync(models)
.filter(function (file) {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js')
})
.forEach(function (file) {
var model = sequelize['import'](path.join(models, file))
db[model.name] = model
})
Object.keys(db).forEach(function (modelName) {
if (db[modelName].associate) {
db[modelName].associate(db)
}
})
db.sequelize.sync({force: false}).then(() => {
console.log("Tables syncronized!!!")
})
module.exports.db = db;
Edit II:
Now, I've got this error:
/home/josecarlos/Workspace/nodejs/remote-roofing/src/server/models/users.js:9
var UserModel = _db["default"].sequelize.define("user", {
^
TypeError: Cannot read property 'define' of undefined
at Object.<anonymous> (/home/josecarlos/Workspace/nodejs/remote-roofing/src/server/models/users.js:4:32)
db.js
import Sequelize from "sequelize";
import UserModel from "./models/users";
import ProjectModel from "./models/projects";
import fs from "fs";
import path from "path";
//It's mandatory to import dotenv in each file where we can use enviroment variables
import config from "dotenv";
config.config();
const sequelize = new Sequelize(process.env.DDBB_NAME, process.env.DDBB_USER,process.env.DDBB_PSWD, {
host: process.env.DDBB_HOST,
port: process.env.DDBB_PORT,
define: {
//freezeTableName: true, /**Don't add 's to the end of each table/model */
//timestamps: false, /**Don't add fields createdAt and updatedAt */
},
dialect: "postgres",
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
}
);
let db = {
sequelize: sequelize,
Sequelize: Sequelize,
models: {}
}
//Register Models
const models = path.join(__dirname, "models");
fs
.readdirSync(models)
.filter(function (file) {
console.log("file: " + file);
return (file.indexOf('.') !== 0) && (file.slice(-3) === '.js')
})
.forEach(function (file) {
var model = sequelize['import'](path.join(models, file))
db[model.name] = model
})
Object.keys(db).forEach(function (modelName) {
if (db[modelName].associate) {
db[modelName].associate(db)
}
})
db.sequelize.sync({force: false}).then(() => {
console.log("Tables syncronized!!!")
})
module.exports.db = db;
users.js
import Sequelize from "sequelize";
import db from "../db";
const UserModel = db.sequelize.define("user", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
email: {
type: Sequelize.STRING,
allowNull: false,
isEmail: {
msg: "The format of the e-mail is not correct"
},
validate: {
notNull: {
msg: "E-mail cannot be empty"
}
}
},
name: {
type: Sequelize.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Name cannot be empty"
}
}
},
surname: {
type: Sequelize.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Surname cannot be empty"
}
}
}
})
UserModel.associate = (models) => {
UserModel.hasMany(models.ProjectModel, {
foreignKey: "userID"
})
}
module.exports.UserModel = UserModel;
project.js
import Sequelize from "sequelize";
import db from "../db";
const ProjectModel = db.sequelize.define("project", {
id: {
type: type.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
name: {
type: type.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Name cannot be empty"
}
}
},
body: {
type: type.TEXT,
allowNull: false,
validate: {
notNull: {
msg: "Body cannot be empty"
}
}
},
status: {
type: type.ENUM("active", "inactive", "declined", "completed"),
allowNull: false,
validate: {
notNull: {
msg: "Status cannot be empty"
}
}
},
userID: {
type: type.INTEGER,
allowNull: false,
validate: {
notNull: {
msg: "userID cannot be empty"
}
}
}
});
ProjectModel.associate = (models) => {
ProjectModel.belongsTo(models.UserModel, {
foreignKey: "userID"
})
}
module.exports.ProjectModel = ProjectModel;
Sorry, but I don't understand anything ... :((
Edit III:
I have checked the keys of db in user.js and I've got nothing, so it's right that I've got thi error, but ... Why have db nothing?
[nodemon] starting `babel-node src/server/server.js`
db:
Your issue is that UserModel in user.js is as a function that returns a sequalize definition.
In the same file you are trying to call UserModel.hasMany on the function that returns your model object instead of on the model object itself.
You probably just want to remove the wrapping function and assign the sequalize object to UserModel directly.
const UserModel = sequelize.define("user", {
...
or move the calls on UserModel into the function.
const model = sequelize.define("user", {
...
});
model.hasMany(ProjectModel, { foreingKey: "userID", sourceKey: "id"});
model.belongsTo(UserModel, { foreingKey: "userID", sourceKey: "id"});
return model;
};
module.exports.UserModel = UserModel;
Related
I was trying to connect and get data from oracle database using sequelize, but it was returning an error Cannot read properties of undefined (reading 'length'): undefined
Here is the code :
Connection
import { Sequelize, DataTypes } from "sequelize";
import _projects from "../models/projects.js";
export const getProjects = async (req, res, next) => {
var conn = new Sequelize({
dialect: 'oracle',
username: dbAdmin,
password: dbPass,
dialectOptions: { connectString: connStr } // also tried { connectionString: connStr }
});
function initModel(connection) {
const projects = _projects.init(connection, DataTypes);
return { projects };
}
var db = initModel(conn);
const all_projects = await db.projects.findAll()
console.log("all_projects", all_projects.rows); // Cannot read properties of undefined (reading 'length'): undefined
}
projects.js
import _sequelize from 'sequelize';
const { Model, Sequelize } = _sequelize;
export default class projects extends Model {
static init(sequelize, DataTypes) {
return super.init({
id: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true
},
credat: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: Sequelize.fn('getdate')
},
description: {
type: DataTypes.STRING(1024),
allowNull: true
},
picture: {
type: DataTypes.STRING(255),
allowNull: true
},
settings: {
type: DataTypes.TEXT,
allowNull: true
},
is_deleted: {
type: DataTypes.INTEGER,
allowNull: true
}
}, {
sequelize,
tableName: 'projects',
schema: 'dbo',
timestamps: false,
});
}
}
It seems like you confused findAll with findAndCountAll which returns rowa and count and also you didn't indicate any parameters at all (which can lead to this error with undefined) in findAll:
Compare:
const all_projects = await db.projects.findAll({})
console.log("all_projects", all_projects);
and
const a_page_of_projects = await db.projects.findAndCountAll({
limit: 10,
offset: 0
})
console.log("a_page_of_projects rows", a_page_of_projects.rows);
console.log("a_page_of_projects count", a_page_of_projects.count);
import { Model, DataTypes } from 'sequelize';
interface IExtracciones {
id_extraccion: Number | null | undefined;
monto_extraido: Number;
fecha_de_extraccion: Date;
numero_de_cuenta: Number;
}
export default class Extracciones extends Model<IExtracciones> {}
Extracciones.init(
{
id_extraccion: {
type: DataTypes.INTEGER,
primaryKey: true,
},
monto_extraido: {
type: DataTypes.INTEGER,
allowNull: false,
},
fecha_de_extraccion: {
type: DataTypes.DATE,
allowNull: false,
},
numero_de_cuenta: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
sequelize: bd,
tableName: 'extracciones',
}
);
function agregaRelacion(m1: typeof Model /*error here*/, m2: any, fk: String) {
m1.hasOne(m2, { foreignKey: fk });
m2.hasOne(m1, { foreignKey: fk });
}
The 'this' context of type 'typeof Model' is not assignable to method's 'this' of type 'ModelStatic<Model<{}, {}>>'.
Type 'typeof Model' is not assignable to type 'new () => Model<{}, {}>'.
Cannot assign an abstract constructor type to a non-abstract constructor type.ts(2684)
import { Model, DataTypes, Sequelize, ModelStatic } from 'sequelize';
interface IExtracciones {
id_extraccion: Number | null | undefined;
monto_extraido: Number;
fecha_de_extraccion: Date;
numero_de_cuenta: Number;
}
export default class Extracciones extends Model<IExtracciones> {}
Extracciones.init(
{
id_extraccion: {
type: DataTypes.INTEGER,
primaryKey: true,
},
monto_extraido: {
type: DataTypes.INTEGER,
allowNull: false,
},
fecha_de_extraccion: {
type: DataTypes.DATE,
allowNull: false,
},
numero_de_cuenta: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
// change new Sequeilze()
sequelize: new Sequelize(),
tableName: 'extracciones',
}
);
// m2 replace any with appropriate modelname if needed
/* corrected fk type (String compiles to javascript, shouldn't used as type) */
function agregaRelacion(m1: ModelStatic<Extracciones>, m2: ModelStatic<any>, fk: string) {
m1.hasOne(m2, { foreignKey: fk });
m2.hasOne(m1, { foreignKey: fk });
}
Solution
imagine we have a users table and our users have skills so it is a one to many
right?
UserModel
import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";
export interface UserAttributes {
id: number;
name: string;
email: string;
password: string;
middleName: string;
lastName: string;
secondLastName: string;
active: boolean;
createdAt?: Date;
updatedAt?: Date;
}
export interface UserModel extends Model<UserAttributes>, UserAttributes {}
export class User extends Model<UserModel, UserAttributes> {}
export type UserStatic = typeof Model & {
new (values?: object, options?: BuildOptions): UserModel;
};
export function UserFactory (sequelize: Sequelize) {
return <UserStatic>sequelize.define("users", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
middleName: {
type: DataTypes.STRING,
allowNull: true,
},
lastName: {
type: DataTypes.STRING,
allowNull: false,
},
secondLastName: {
type: DataTypes.STRING,
allowNull: true,
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
});
}
SkillsModel
import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";
export interface SkillsAttributes {
skill: string;
}
export interface SkillsModel
extends Model<SkillsAttributes>,
SkillsAttributes {}
export type SkillsStatic = typeof Model & {
new (values?: object, options?: BuildOptions): SkillsModel;
};
export function SkillsFactory (sequelize: Sequelize) {
return <SkillsStatic>sequelize.define("skills", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
skill: {
type: DataTypes.STRING,
unique: true,
},
});
}
now imagine we have a dir call anyName and in there we have
we are going to export users and skills from our index file
index
import { Sequelize } from "sequelize";
import { CourseFactory, CourseStatic } from "./courses";
import { EducationFactory, EducationStatic } from "./education";
import { ExperienceFactory, ExperienceStatic } from "./experience";
import { FieldsFactory, FieldsStatic } from "./fields-of-interets";
import { GeneralFactory, GeneralStatic } from "./general";
import { SkillsFactory, SkillsStatic } from "./skills";
import { SkillsTypeFactory, SkillsTypeStatic } from "./skills-type";
import { SocialMediaFactory, SocialMediaStatic } from "./social-media";
import { UserFactory, UserStatic } from "./users";
export interface DB {
sequelize: Sequelize;
User: UserStatic;
Skills: SkillsStatic;
SkillsType: SkillsTypeStatic;
Experience: ExperienceStatic;
Education: EducationStatic;
Course: CourseStatic;
General: GeneralStatic;
SocialMedia: SocialMediaStatic;
FieldsOfInterest: FieldsStatic;
}
const sequelize = new Sequelize(
(process.env.DB_NAME = "rest_resume_api"),
(process.env.DB_USER = "john"),
(process.env.DB_PASSWORD = "password"),
{
port: Number(process.env.DB_PORT) || 54320,
host: process.env.DB_HOST || "localhost",
dialect: "postgres",
pool: {
min: 0,
max: 5,
acquire: 30000,
idle: 10000,
},
}
);
const User = UserFactory(sequelize);
const Skills = SkillsFactory(sequelize);
const SkillsType = SkillsTypeFactory(sequelize);
const Experience = ExperienceFactory(sequelize);
const Education = EducationFactory(sequelize);
const Course = CourseFactory(sequelize);
const General = GeneralFactory(sequelize);
const SocialMedia = SocialMediaFactory(sequelize);
const FieldsOfInterest = FieldsFactory(sequelize);
SkillsType.belongsTo(Skills);
SkillsType.belongsToMany(User, { through: "users_has_skills" });
User.belongsToMany(SkillsType, { through: "users_has_skills" });
Experience.belongsToMany(User, { through: "user_has_experience" });
User.belongsToMany(Experience, { through: "user_has_experience" });
Education.belongsToMany(User, { through: "user_has_education" });
User.belongsToMany(Education, { through: "user_has_education" });
Course.belongsTo(User);
General.belongsTo(User);
SocialMedia.belongsToMany(User, { through: "user_has_social" });
User.belongsToMany(SocialMedia, { through: "user_has_social" });
FieldsOfInterest.belongsToMany(User, { through: "user_has_fields" });
User.belongsToMany(FieldsOfInterest, { through: "user_has_fields" });
export const db: DB = {
sequelize,
User,
Skills,
};
I am trying to create a many to many relationship between user table and role table through userroles table.
After table creation the db looks just fine, I tried pretty much everything I found on the sequelize documentation and previous answers here, nothing seems to work.
I am getting this error: EagerLoadingError [SequelizeEagerLoadingError]: UserRoles is not associated to User!
Any idea of what am I doing wrong ? please help!
class User extends Model {
static associate(models) {
User.belongsToMany(models.Role, {
foreignKey: "user_id",
through:'UserRoles',
as:"users"
});
}
}
User.init(
{
user_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique: true,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
phone: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "User",
}
);
class Role extends Model {
static associate(models) {
Role.belongsToMany(models.User, {
foreignKey: "role_id",
through:'UserRoles',
as:"roles"
});
}
}
Role.init(
{
role_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique:true
},
role_name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
role_desc: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "Role",
}
);
class UserRoles extends Model {
static associate(models) {
}
}
UserRoles.init(
{
userroles_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
// user_id: {
// type: DataTypes.UUID,
// defaultValue: DataTypes.UUIDV4,
// },
// role_id: {
// type: DataTypes.UUID,
// defaultValue: DataTypes.UUIDV4,
// },
},
{
sequelize,
modelName: "UserRoles",
}
);
const signup = (req, res) => {
console.log(req.body);
console.log("signup entry");
if (
!req.body.role ||
!req.body.email ||
!req.body.password ||
!req.body.name ||
!req.body.phone
) {
res.status(400).send({
msg: "Please pass role, email, password and name.",
});
} else {
sequelize.models.User.findOne({
where: {
email: req.body.email,
},
})
.then((duplicateemailfound) => {
if (duplicateemailfound) {
console.log(duplicateemailfound);
return res.status(400).json({
success: false,
message: "Email already registered",
});
} else {
let userRole = req.body.role.toLowerCase();
console.log("userRole:", userRole);
sequelize.models.Role.findOne({
where: {
role_name: userRole,
},
})
.then((foundRole) => {
// console.log(foundRole);
if (foundRole == null) {
return res.status(400).json({
success: false,
role: "null or not found",
});
}
// console.log("foundRole", foundRole); // .role_id
let addUser = {
email: req.body.email,
password: req.body.password,
name: req.body.name,
phone: req.body.phone,
role_id: foundRole.role_id,
};
sequelize.models.User.create(addUser, {
include: [{ model: sequelize.models.UserRoles }],
})
.then((newUser) => {
console.log("new user", newUser);
return res.status(201).json({
success: true,
newuser: newUser,
});
})
.catch((error) => {
console.log(error);
res.status(400).json({
success: false,
// message: "Duplicate Email was Found",
error: error.errors[0].message,
error: error,
});
});
})
.catch((error) => {
console.log(error);
res.status(400).json({
error: error,
msg: "bbb",
});
});
}
})
.catch((err) => {
console.log(err);
});
}
};
You create some a class for each Model and extend them with Model class of sequelize, this if fine.
Now, you define a static method inside the class named associate(model) where you define the rule for that class. This is fine because you used static which is required here to since it will be a property of the class, not of an object.
Then you call the initialize method (a in-built method of class Model). In the same way you need to call your defined associate.
Here is a problem, because in the structure that you have now, you can't call that method in it's own class file, becuase you need the other Model to pass it as parameter.
So there are 2 solutions:
Import your User model inside Role model file and call the static method, like this:
const User = require('User')
class Role extends Model {
static associate(model) {
Role.belongsToMany(model, {
foreignKey: "role_id",
through:'UserRoles',
as:"roles"
});
}
}
Role.init(
{
role_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique:true
},
role_name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
role_desc: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "Role",
}
);
Role.associate(User);
This will use your User model to pass it to the static method and finally to run the belongsToMany
Second solution would be to create an index file for your Models, where you import both of them and you can simply run that belongsToMany there, and then simply import that index file in the main file of your application, something like this:
User.js
const index = require('./../index.js');
const Sequelize = require('sequelize');
const Model = Sequelize.Model;
const sequelize = index.sequelize;
class User extends Model{}
User.init({
username: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
password: {
type: Sequelize.STRING,
allowNull: false
},
role: {
type: Sequelize.STRING,
allowNull: false
}
},{
sequelize,
modelName: 'user'
});
module.exports = {
User: User
}
Role.js will look the same but with your own model.
and index.js would look like this:
const Sequelize = require('sequelize');
const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USERNAME, process.env.DB_PASSWORD, {
host: process.env.DB_HOST,
dialect: process.env.DB_DIALECT
});
exports.sequelize = sequelize;
const user = require('./models/User');
const role= require('./models/Role');
role.belongsToMany(user, {
foreignKey: "role_id",
through:'UserRoles',
as:"roles"
});
sequelize.sync(user);
sequelize.sync(role);
exports.db = {
user: user,
role: role
}
I'm using Sequelize for my Postgres database. I have a Messages and a Users table; a user has many messages and a message belongs to a user. I've defined my models as follows:
User
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true,
}
},
password: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [8, 50],
}
},
}, {
modelName: 'User',
});
User.associate = (models) => {
User.hasMany(models.Message, { foreignKey: 'userId', as: 'Messages' })
}
return User;
};
Message
module.exports = (sequelize, DataTypes) => {
const Message = sequelize.define('Message', {
content: {
allowNull: false,
type: DataTypes.STRING,
validate: {
len: [1, 248],
}
},
userId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
likes: {
defaultValue: 0,
type: DataTypes.INTEGER
},
}, {
modelName: 'Message',
});
Message.associate = (models) => {
Message.belongsTo(models.User, { foreignKey: 'userId', as: 'User', onDelete: 'CASCADE' })
}
return Message;
};
And here's how I'm testing them:
User.create({
firstName: 'Test', lastName: 'Test', email: 'test#test.com', password: '87654321'
}).then((newUser) => {
console.log(newUser.get())
})
Message.bulkCreate([
{ content: "Hello", likes: 0, userId: 1 },
{ content: "Hello World", likes: 0, userId: 1 },
{ content: "Hello World 2", likes: 123, userId: 1 }
])
.then((newMessages) => {
console.log(newMessages)
})
const findAllWithMessages = async () => {
const users = await User.findAll({
include: [{
model: Message
}]
});
console.log(JSON.stringify(users, null));
}
Here's my Migration file to create the users table:
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstName: {
type: Sequelize.STRING,
allowNull: false
},
lastName: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true,
}
},
password: {
type: Sequelize.STRING,
allowNull: false,
validate: {
len: [8, 50],
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Users');
}
};
And the messages table:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Messages', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id',
as: 'userId',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
},
content: {
allowNull: false,
type: Sequelize.STRING,
validate: {
len: [1, 248],
}
},
likes: {
defaultValue: 0,
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Messages');
}
};
I'm registering my models and associations using Sequelize CLI out of the box code when you run sequelize-cli init:
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
const User = require('./user')(sequelize, Sequelize.DataTypes)
const Message = require('./message')(sequelize, Sequelize.DataTypes)
db.models = { User, Message }
db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.DataTypes = Sequelize.DataTypes
module.exports = db;
Finally, when I run findAllWithMessages(), I'm getting this error UnhandledPromiseRejectionWarning: SequelizeEagerLoadingError: Message is not associated to User!
I can confirm that the models are being created and that the association between the models work because when I run a raw SQL query select * from "Messages" as a inner join "Users" as b on b.id = a."userId" where a."userId"=1; I get the correct results. So I'm assuming its a Sequelize thing.
Any help is appreciated!
I've found my issue. In the code I was importing from db.models = { User, Message } so this block Object.keys(db).forEach(modelName)... wasn't associating the models I was using. Essentially, I was calling the .associate function on instances of the models that were different than the instances I was using.
I'm newbie with sequelize and I'm trying to assiacite two tables. Users and Projects. I have defined a model for each one called UserModel and ProjectModel and when I try to associate projects with user I've got this error:
belongsTo called with something that's not a subclass of
Sequelize.Model
These are the files where I define each model:
user.js
module.exports = (sequelize, DataType) => {
const UserModel = sequelize.define("user", {
id: {
type: DataType.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
email: {
type: DataType.STRING,
allowNull: false,
isEmail: {
msg: "The format of the e-mail is not correct"
},
validate: {
notNull: {
msg: "E-mail cannot be empty"
}
}
},
name: {
type: DataType.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Name cannot be empty"
}
}
},
surname: {
type: DataType.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Surname cannot be empty"
}
}
}
});
UserModel.associate = (models) => {
UserModel.hasMany(models.ProjectModel, {
foreignKey: "userID"
})
}
return UserModel;
};
project.js
module.exports = (sequelize, DataType) => {
const ProjectModel = sequelize.define("project", {
id: {
type: DataType.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
name: {
type: DataType.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Name cannot be empty"
}
}
},
body: {
type: DataType.TEXT,
allowNull: false,
validate: {
notNull: {
msg: "Body cannot be empty"
}
}
},
status: {
type: DataType.ENUM("active", "inactive", "declined", "completed"),
allowNull: false,
validate: {
notNull: {
msg: "Status cannot be empty"
}
}
},
userID: {
type: DataType.INTEGER,
allowNull: false,
validate: {
notNull: {
msg: "userID cannot be empty"
}
},
references: {
model: UserModel,
key: "id"
}
}
});
ProjectModel.associate = (models) => {
ProjectModel.belongsTo(models.UserModel, {
foreignKey: "userID"
});
}
ProjectModel.belongsTo(UserModel, {
foreignKey: "userID"
});
return ProjectModel;
}
What am I doing wrong?
Finally, I have found the solution to my problem. In this association, for example:
ProjectModel.associate = (models) => {
ProjectModel.belongsTo(models.UserModel, {
foreignKey: "userID"
});
}
With the variable models.UserModel I was making reference to the variable which is returned by the model. Instead of this, you have to write the name of the table. So, in my case the correct function is:
ProjectModel.associate = (models) => {
ProjectModel.belongsTo(**models.users**, {
foreignKey: "userID"
});
}
Don't import model in another model file. like if you wrote A.belongsTo(B) file,then don't import A file into B file.