avatarJorge Javier Garcia Ruiz

Summary

The provided content discusses troubleshooting a Sequelize association error in a NodeJS+Express project, offering a solution to correctly define one-to-many relationships between database tables.

Abstract

The article addresses a common error encountered in Sequelize when defining associations between models: .hasMany called with something that’s not a subclass of Sequelize.Model. The author explains that the error arises from improper model relationship definitions, particularly when using circular references in models like Team and Player. The traditional approach of using .hasMany() and .belongsTo() in both models can lead to this error. The author proposes a solution involving the use of an associate function within each model file, which is then called after all models are imported into a central index.js file. This method allows for more flexible and detailed relationship definitions without causing errors during complex queries like joins. The article also corrects a mistake found in an example, where Sequelize's import method is incorrectly used, and provides a working alternative using Node's require function. The solution is credited to a Stack Overflow user, David Kamer, and the author hopes that sharing their experience will help others resolve similar issues more efficiently.

Opinions

  • The author suggests that defining relationships in both associated models can lead to errors and prefers defining them in a single model.
  • The least restrictive approach that allows for detailed relationships is favored by the author.
  • The author values the use of an associate function and centralized model importation for cleaner and more maintainable code.
  • The author acknowledges the importance of the order in which relationships are defined, as it affects query capabilities.
  • The author appreciates the community's contributions, specifically referencing a solution provided by David Kamer on Stack Overflow.
  • The author believes that the shared solution will be beneficial for future reference and for others facing similar Sequelize errors.

Sequelize models association

.hasMany called with something that’s not a subclass of Sequelize.Model

Photo by Reza Namdari on Unsplash

Recently while working on a NodeJS+Express project I faced this error:

.hasMany called with something that’s not a subclass of Sequelize.Model

But first a little context, I had like 8 to 10 database tables, mostly with one to many relationships. According to Sequelize official documentation, there are 2 ways to implement this relationship: .hasMany() and belongsTo(). The classical example being having a Team with Players it led go something like this:

Team.hasMany(Player);
Player.belongsTo(Team);

However, if you are creating/defining your model this way:

//teamModel.js
const { DataTypes } = require(‘sequelize’);
const sequelize = require(‘../config/database’);
const Team = sequelize.define(‘Team’, {fields…}, {
  tableName: ‘Team’,
  freezeTableName: true,
  timestamps: false
});
Team.hasMany(Player);
module.exports = Team;

and

//playerModel.js
const { DataTypes } = require(‘sequelize’);
const sequelize = require(‘../config/database’);
const Player = sequelize.define(‘Player’, {fields…}, {
  tableName: ‘Player’,
  freezeTableName: true,
  timestamps: false
});
Player.belongsTo(Team);
module.exports = Player;

Doing this will more than likely throw the error mentioned above, first of all we are creating a circular reference by defining this relationship on both models.

If we simple leave this relationship in one of the models, only using for example belongsTo, we won’t get an error. But the downside of this approach is that if you require to use a complex query like a join, and you first use the model that doesn’t have the relationship defined you will then get an error stating that those models are not related, since the order you define your relationships is relevant.

In my case the least restrictive approach that also allows me to define very detailed relationships for every model was mentioned here. But I’ll explain it here in any case:

//team.js
module.exports = (sequelize, DataTypes) => {
  const Team = sequelize.define(‘Team’, {fields…}, {});
  Team.associate = function(models) {
    Team.hasMany(models.Player);
  };
  return Team;
};

and adding this file:

//index.js
‘use strict;
var fs = require(‘fs’);
var path = require(‘path’);
var Sequelize = require(‘sequelize’);
var basename = path.basename(module.filename);
var env = process.env.NODE_ENV || ‘development’;
var config = require(__dirname + ‘/../config/server-config.json’)[env];
var db = {};
if (config.use_env_variable) {
var sequelize = new Sequelize(process.env[config.use_env_variable]);
} else {
var sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(function(file) {
  return (file.indexOf(‘.’) !== 0) && (file !== basename) && (file.slice(-3) === ‘.js’);
})
.forEach(function(file) {
  var model = sequelize[‘import’](path.join(__dirname, file));
  db[model.name] = model;
});
Object.keys(db).forEach(function(modelName) {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;

So what is happening in this last file, this one is in charge of reading all the files in the same folder and import them as a sequelize model and add it to our db object. This way worked for me and allowed me to define my relationships more freely.

Also, this line:

var model = sequelize[‘import’](path.join(__dirname, file));

Actually throw an error, apparently Sequelize didn’t have import defined, changing it to Node’s built in CommonJS require function did the trick.

const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes)

I got this solution from David Kamer’s response to this Stack Overflow question.

Hopes this can helps someone and that at the very least helps me fix this issue faster in the future. Thanks for reading!!!

Coding
Programming
Technology
Recommended from ReadMedium