2

Why is it that in some of my models, sequelize WON’T create a new column for foreignkey? BUT it does create for other models??? It’s frustrating and weird. For instance, in this User model, sequelize won’t create role_id.

'use strict';
module.exports = (sequelize, DataTypes) => {
  const User = sequelize.define('User', {
    id: { type: DataTypes.BIGINT, allowNull: false, autoIncrement: true, unique: true, primaryKey: true },
    first_name: DataTypes.STRING,
    last_name: DataTypes.STRING
  }, {});
  User.associate = function(models) {
    User.belongsTo(models.Role, { foreignKey: 'role_id' });
  };
  return User;
};

This is a similar question: Sequelize not creating model association columns BUT! It wasn't answered.

I've spent hours on this, I did everything like:

  1. Reading this thoroughly: https://sequelize.org/master/manual/assocs.html
  2. Experimenting, like creating a new dummy model, with name NewUser. It works! But again not with User name.
  3. Posted on Sequelize's Slack channel.

After this Stackoverflow question, I will seek help from their Github's issue page.

I'm thinking I can just define the column role_id instead of adding it through the associate function.

Glenn Posadas
  • 12,555
  • 6
  • 54
  • 95
  • Can you show differences between a model that has created FK and a model without created FK? Is there a difference in a registration process of these two models? – Anatoly May 10 '20 at 09:10
  • Hey Antoly, literally no difference at all! Like I mentioned, I tried creating a dummy model with the same content BUT different filename, like ```ZUser.js``` (model defined as ```ZUser```, and I can see the `roldeId` column being added by Sequelize. I really now don't know what's happening. I think it's because of the class name defined as ```User```???? – Glenn Posadas May 10 '20 at 09:14
  • Maybe when you run 'sync' User table already exists and sequelize does not alter it while new ZUser model leads to creating a new table with all columns and FKs that are presenting in it at the moment – Anatoly May 10 '20 at 09:24
  • I do sync force true when I'm in development/local. Like this: ```db.sequelize.sync({ force : true})```. I also have tried deleting ALL the tables (But haven't tried re-creating database). I also tried deleting migrations, and re-running db:migrate/ – Glenn Posadas May 10 '20 at 09:26
  • Wait! You either use 'sync' or migrations but not both! – Anatoly May 10 '20 at 09:28
  • I see. Thanks for that! I didn't actually know that. ANYWAYS, I found the answer. I commented out the line ```require("./app/routes/user/user.routes")(app)``` and ```require("./app/routes/auth/auth.routes")(app)``` in my ```server.js``` file that both use my ```User``` defined model! When I did this, the ```roleId``` column was generated!!! Maybe it's because of the ```passport.js```? – Glenn Posadas May 10 '20 at 09:39
  • I think you should register and collect all models at one module and then import it everywhere where you need to access regisitered models. – Anatoly May 10 '20 at 09:41
  • Thanks, Antoly! I'm still thinking carefully how to do that. No idea but I'm sure I'll get there. – Glenn Posadas May 10 '20 at 10:12

2 Answers2

6

All models should be registered in one place as long as their associations:

database.js

const fs = require('fs')
const path = require('path')
const Sequelize = require('sequelize')
const db = {}
const models = path.join(__dirname, 'models') // correct it to path where your model files are

const sequelize = new Sequelize(/* your connection settings here */)

fs
  .readdirSync(models)
  .filter(function (file) {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js')
  })
  .forEach(function (file) {
    // Sequelize version <= 5.x
    var model = sequelize['import'](path.join(models, file))
    // Sequelize version >= 6.x
    // var model = require(path.join(models, file))(
    //   sequelize,
    //   Sequelize.DataTypes
    // );
    db[model.name] = model;
  })

Object.keys(db).forEach(function (modelName) {
  if (db[modelName].associate) {
    db[modelName].associate(db)
  }
})

db.Sequelize = Sequelize // for accessing static props and functions like Op.or
db.sequelize = sequelize // for accessing connection props and functions like 'query' or 'transaction'

module.exports = db

some_module.js

const db = require('../database')
...
const users = await db.user
   .findAll({
     where: {
      [db.Sequelize.Op.or]: [{
        first_name: 'Smith'
      }, {
        last_name: 'Smith'
      }]
     }
})

Anatoly
  • 20,799
  • 3
  • 28
  • 42
  • Thanks, Anatoly. But I think I'm doing the right thing, because this registration thing was generated by Sequelize too! The solution actually is to register the routes after the `db.sync`. – Glenn Posadas May 10 '20 at 10:55
  • Where the `basename` variable come from? – TheUnreal Sep 20 '20 at 13:17
  • var basename = path.basename(module.filename) – Anatoly Sep 20 '20 at 13:24
  • @Anatoly I can see `TypeError: sequelize.import is not a function` – KIRAN K J Nov 27 '21 at 15:58
  • @Anatoly Meanwhile I went through some similar posts. I tried to incorporate those. But some minor errors blocked me. Do you have any suggestions to refer to any complete sample code? – KIRAN K J Nov 27 '21 at 17:08
  • In Sequelize 6 they removed `import` function. You need to call model functions yourself directly passing Sequelize instance and DataTypes. – Anatoly Nov 27 '21 at 19:12
  • @Anatoly thanks for pointing me back to this post, I have read it before but now it makes more sense except for one thing, the associate static function on each model *by default* expects a model dictionary as param, instead you passed as a param the entire db object that you created, I assume that you had yo change the models code to extract the models only and then associate them correctly right? – Gabriel Oliveira Mar 09 '23 at 00:13
  • It's because `db` object is indeed the models dictionary and also it stores the prop of Sequelize instance. – Anatoly Mar 09 '23 at 17:40
3

Thanks to Anatoly for keeping up with my questions about Sequelize like this.

After so many trials and errors, I figured that the issue was caused by the registration of my routes like:

require("./app/routes/user/user.routes")(app)

in my app.js or server.js. These routes registration was added before the db.sync!

So what I did was, I call these routes registration after that db.sync, like so:

const db = require("./app/models")

if (process.env.NODE_ENV === "production") {
  db.sequelize.sync().then(() => {
    useRoutes()
  })
} else {
  db.sequelize.sync({ force: true }).then(() => {
    console.log("Drop and re-sync db.")
    useRoutes()
  })
}

function useRoutes() {
  console.log("Use routes...")
  require("./app/routes/user/user.routes")(app)
  require("./app/routes/auth/auth.routes")(app)
}

Voila, fixed!

Glenn Posadas
  • 12,555
  • 6
  • 54
  • 95