4

I have a user table that has a foreign key column to a roles table.

I defined all relationships in mysql and using sequelize-auto I generated my models.

The generated model for user was this:

  const user = sequelize.define('user', {
    Id: {
      type: DataTypes.INTEGER(11),
      allowNull: false,
      primaryKey: true,
      autoIncrement: true,
    },
    Email: {
      type: DataTypes.STRING(45),
      allowNull: false,
      unique: true,
    },
    RoleId: {
      type: DataTypes.INTEGER(11),
      allowNull: false,
      references: {
        model: 'roles',
        key: 'Id',
      },
    },
  });

I thought that my reference was set so that when I did the following in my resolver:

users: async () => {
  const users = await db.user.findAll({
    include: [
      {
        model: db.roles,
      },
    ],
  });

return users

I should have gotten back a list of user roles with the following query in the playground:

{users
  {roles
   {Name, Id}
  }
}

instead I got

roles is not associated to user!

What I later figured out is that I needed to make an association:

  user.associate = models => {
    user.hasMany(models.roles, {
      foreignKey: 'Id',
      sourceKey: 'RoleId',
      onDelete: 'cascade',
    });
  };

Then it worked.

What I still dont understand is what is this for in the user-model?:

 references: {
    model: 'roles',
    key: 'Id',
  },

I thought that it was my "association" with the roles table but without me explicitly adding an association this simply did nothing. Can someone please explain the meaning of references field?

CodingLittle
  • 1,761
  • 2
  • 17
  • 44

2 Answers2

2

references is for describing a model in a migration and for auto-creating tables using the sync function. To manipulate data (not a structure) you use associations like user.hasMany

Anatoly
  • 20,799
  • 3
  • 28
  • 42
  • So it¨s a deskription but if Im focusing on being practical only in my backend enviorment and creating all relationships in the mysql workbench then this field is useless for me? If I however want to create tables from my backend to my mysql enviorment then this is of use? – CodingLittle Apr 09 '20 at 17:25
  • If you will use migrations to create and modify your DB structure then you won't need 'references' in the models at all. All these references will be in migrations to help sequelize CLI to create foreign keys. – Anatoly Apr 09 '20 at 18:19
  • 1
    Current version 0.7.5 of `sequelize-auto` generates the associations automatically. – Steve Schmitt Dec 05 '20 at 17:17
0

The given answer is correct but I realized later on that the reason for this question was that I assumed I could create assosiations via code so that I did not have to modify every model manualy by default.

For those of you looking to do the same I found a solution here.

Which is basically:

1) inside of your models folder create an index.js file and add the following code

import Sequelize from 'sequelize';

const fs = require('fs');
const path = require('path');

const basename = path.basename(__filename);

const db = {};

// @ts-ignore
const sequelize = new Sequelize('dbname', 'dbUser', 'password', {
  host: '127.0.0.1',
  port: 'PORT',
  dialect: 'mysql',
  define: {
    freezeTableName: true,
    timestamps: false,
  },
  pool: {
    max: 5,
    min: 0,
    acquire: 30000,
    idle: 10000,
  },
  // <http://docs.sequelizejs.com/manual/tutorial/querying.html#operators>
  operatorsAliases: false,
});

const tableModel = {};

fs.readdirSync(__dirname)
  .filter(file => file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js')
  .forEach(file => {
    const model = sequelize.import(path.join(__dirname, file));
    db[model.name] = model;
    tableModel[model.name] = model;
  });

Object.getOwnPropertyNames(db).forEach(modelName => {
  const currentModel = db[modelName];
  Object.getOwnPropertyNames(currentModel.rawAttributes).forEach(attributeName => {
    if (
      Object.prototype.hasOwnProperty.call(
        currentModel.rawAttributes[attributeName],
        'references'
      ) &&
      Object.prototype.hasOwnProperty.call(
        currentModel.rawAttributes[attributeName].references,
        'model'
      ) &&
      Object.prototype.hasOwnProperty.call(
        currentModel.rawAttributes[attributeName].references,
        'key'
      )
    ) {
      if (
        !(
          currentModel.rawAttributes[attributeName].references.model &&
          currentModel.rawAttributes[attributeName].references.key
        )
      ) {
        console.log(
          `*SKIPPED* ${modelName} ${attributeName} references a model ${currentModel.rawAttributes[attributeName].references.model} with key ${currentModel.rawAttributes[attributeName].references.key}`
        );
        return;
      }

      console.log(
        `${modelName} ${attributeName} references a model ${currentModel.rawAttributes[attributeName].references.model} with key ${currentModel.rawAttributes[attributeName].references.key}`
      );
      const referencedTable =
        tableModel[currentModel.rawAttributes[attributeName].references.model];

      currentModel.belongsTo(referencedTable, { foreignKey: attributeName });
      referencedTable.hasMany(currentModel, { foreignKey: attributeName });

    }
  });
});

// @ts-ignore
db.sequelize = sequelize;
// @ts-ignore
db.Sequelize = Sequelize;

// eslint-disable-next-line eol-last
module.exports = db;

2) inside of your resolver just reference the above:

const db = require('../assets/models/index');
CodingLittle
  • 1,761
  • 2
  • 17
  • 44