Sequelize one to many assocation in multiple files

I am working on one to many assocations with sequelize. Most tutorials and documentation shows examples when both models are defined in the same file.
I currently have two files, first city.js:

const Promise = require('bluebird');
var Country = require('./country');

var City = sequelize.define("City", {
  id: {
    type: DataTypes.INTEGER,
    field: 'id',
    primaryKey: true,
    autoIncrement: true
  },...
}, {
  freezeTableName: true,
  timestamps: false
});

City.belongsTo(Country, {foreignKey : 'countryId', as: 'Country'});

Promise.promisifyAll(City);
module.exports = City;

And a second file country.js:

const Promise = require('bluebird');
var City = require('./city');

var Country = sequelize.define("Country", {
  id: {
    type: DataTypes.INTEGER,
    field: 'id',
    primaryKey: true,
    autoIncrement: true
  },
  ...
}, {
  freezeTableName: true,
  timestamps: false,
  paranoid: false
});

Country.hasMany(City, {foreignKey : 'countryId', as: 'Cities'});

Promise.promisifyAll(Country);
module.exports = Country;

When I import both modules and try to instantiate object:

var City = require('../model/external/city');
var CountryRepository = require('../repository/external/countryRepository');

CountryRepository.findById(1).then(function(country) {
    var city = City.build();
    city.name = 'Paris';
    city.setCountry(country);
    console.log('OK');
});

I get the following error:

throw new Error(this.name + ‘.’ +
Utils.lowercaseFirst(Type.toString()) + ‘ called with something
that\’s not an instance of Sequelize.Model’)

Is the problem that models are promisified before they are exported from model or am I missing something?

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

I’m not sure what exactly is the problem with your code, would need to run it to be sure.

But as you were looking for an example, take a look at this example from Sequelize Github.

It declares models in different files and associate them in the index.js.

Later you can reference your other model with a simple model atribute model.Country:

City.belongsTo(model.Country, {foreignKey : 'countryId', as: 'Country'});

For instance, user.js.

Solution 2

You have to declare the associations after having export all your models

I had the same problem!

Solution 3

I created a technique that I think is really nice and even allows for composite unique keys across even a foreign key.

I’ll give an example with a basic user.js model class file

models/user.js

const { Model, DataTypes } = require('sequelize');
module.exports = function(sequelize){
    class User extends Model {}
    return User.init({
      id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true
      },
      name: {
          type: DataTypes.STRING,
          allowNull: false
      },
      trading_system_key: {
          type: DataTypes.STRING,
          allowNull: false
      },
    }, {
      sequelize,
      modelName: 'User',
      indexes: [{ unique: true, fields: ['trading_system_key'] }]
    });
};

This shows a simple example of the user class, no associations yet, but the next file will use it.

models/algorithm.js

const { Model, DataTypes } = require('sequelize');
const User = require('../models/user');

module.exports = function(sequelize){
    class Algorithm extends Model {}
    UserModel = User(sequelize);//@JA - Gets a defined version of user class

    var AlgorithmFrame = Algorithm.init({
        id: {
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
        },
        name: {
            type: DataTypes.STRING,
            allowNull: false,
        },
        user_Id: {
            type: DataTypes.INTEGER,
            references: { 
                model: UserModel,
                key: 'id',
            },
        }
    }, {
      sequelize,
      modelName: 'Algorithm',
      indexes: [{ unique: true, fields: ['name','user_id'] }]
    });

    return AlgorithmFrame
};

This example shows how you can create a foreign key reference without having to say belongsTo or HasOne etc… This comes in handy when you need to create composite keys.

For example, note that I put indexes: [{ unique: true, fields: ['name','user_id'] }]

This makes it so the name and user_id can’t be the same together (a composite key). So for example if the name was Joe and the user_id was 1 and I tried to create another record with the same details it would reject it, but if I created something with the name Rachel and user_id 1 it would allow it, since it’s only unique if both are the same.

Doing a composite key is not possible to my knowledge unless you set it up this way.

The other advantage of this technique is the models are in a separate file and defined as classes!

So how do I use these?

(In your file you define the sequelize connection…)
api.js

const { Sequelize, DataTypes, Model } = require('sequelize');
const User = require('../models/user');
const Algorithm = require('../models/algorithm');

const sequelize = new Sequelize('database', 'username', 'passwordhere', {
      host: 'db',
      dialect: 'mysql' /* one of 'mysql' | 'mariadb' | 'postgres' | 'mssql' */
    });

const UserModel = User(sequelize);
const AlogorithmModel = Algorithm(sequelize);

(async () => {
      try {
        await sequelize.authenticate();
        await UserModel.sync({ alter: true });
        await AlogorithmModel.sync({ alter: true });

        //Do something with the models now....
        //Etc...

      } catch (error) {
        console.error('Unable to connect to the database:', error);
      }

});

This technique works because of how the class files are defined. In typical software development you include the class and have reference to it and instantiate it, but in this case we are passing in the instantiated version of it already

For example, you will note in the class file I define the class user extends model {} and it’s simply empty inside. Then we return an instantiated version of the class. When you use module.exports in this way it is actually expecting a parameter of the sequelize connection instance.

This is why in the api.js file I say const User = require('../models/user');, this gets reference to the ‘function’ that the file is returning.

The function doesn’t do anything unless you pass it sequelize, so the next line you see…

const UserModel = User(sequelize);

By doing this you are able to have your models in separate files and have them be classes and they can still have associations and even composite indexes!

Hope this helps you out.

UPDATE::

If you want the onUpdate or onDelete cascade option simply define it with the model like this.

 algorithm_id: {
            type: DataTypes.INTEGER,
            allowNull: false,
            onDelete: 'CASCADE',
            onUpdate: 'CASCADE',
            references: { 
                model: AlgorithmModel,
                key: 'id',
            }
        },

You can also set if you want it to be null or not, HOWEVER, keep in mind that allowNull will NOT work with the ‘alter’ table command unless the foreign key has been DROPPED first! the onUpdate and onDelete appear to work with alter just fine though.

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply