Myles Smith
Myles Smith

Reputation: 51

How to associate models in ES6 syntax using sequelize v6

I am using sequelize ORM 6.4.0 and CLI 6.2.0 in an express/nuxt web app. (Also: Node 14.15.3, npm 6.14.11).

Background: Goal is to make a web app for creating mariokart tournaments and track data about individual racers performance on different tracks, vehicles, etc. Issue is around postgres/sequelize associations and creating/using them correctly.

I'm trying to create a "racer" model that has a default vehicle which is from the Vehicle table. I'm using postgres for my tables and am having no trouble creating or interacting with my tables using express and sequelize for the API.

Here is my racer.js model:

'use strict'
const {
  Model
} = require('sequelize')
module.exports = (sequelize, DataTypes) => {
  class Racer extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate (models) {
      // define association here
      this.belongsTo(models.Vehicle, {
        foreignKey: 'vehicleName',
        as: 'defaultVehicle'
      })
    }
  };
  Racer.init({
    name: DataTypes.STRING,
    nickname: DataTypes.STRING
  }, {
    sequelize,
    modelName: 'Racer'
  })
  return Racer
}

My vehicle.js model:

'use strict'
const {
  Model
} = require('sequelize')
module.exports = (sequelize, DataTypes) => {
  class Vehicle extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate (models) {
      // define association here
      this.hasMany(models.Racer)
    }
  };
  Vehicle.init({
    vehicleName: DataTypes.STRING,
    vehicleSize: DataTypes.STRING,
    vehicleClass: DataTypes.STRING,
    vehicleClass2: DataTypes.STRING,
    vehicleSpeed: DataTypes.INTEGER,
    vehicleWeight: DataTypes.INTEGER,
    vehicleAcceleration: DataTypes.INTEGER,
    vehicleHandling: DataTypes.INTEGER,
    vehicleDrift: DataTypes.INTEGER,
    vehicleOffroad: DataTypes.INTEGER,
    vehicleMiniturbo: DataTypes.INTEGER
  }, {
    sequelize,
    modelName: 'Vechile'
  })
  return Vehicle
}

When I view my tables in pgadmin I don't see a defaultVehicle column and if I try to seed the value I thrown "ERROR: column "defaultVehicle" of relation "Racers" does not exist"

I've tried messing around with the names in case it was a capitalization/pluralization thing but that doesn't seem to be the case. I'm struggling to find any documentation that helps, most sequelize docs are using the old .define syntax (I'm trying this primarily because this is the skeleton that gets generated when I use the CLI and it seems like best practices moving forward). What am I missing? Thanks in advance if you can help I'm going crazy and about to just skip the table associations all together and pull all the data then code validations manually.

Upvotes: 5

Views: 10241

Answers (2)

Dimitry COULIBALY
Dimitry COULIBALY

Reputation: 29

In Your Racer migration table

vehicleId: {
    type: Sequelize.INTEGER,
    references: {
      model: 'Vehicle',
      key: 'id'
    }
  },

and in your Racer model

static associate (models) {
  // define association here
  this.belongsTo(models.Vehicle)
}

Upvotes: 0

andres_v
andres_v

Reputation: 409

I had a very similar issue which I solved using the migration file. This is what I did adapted to the tables and data of the question:

In racer.js model:

static associate (models) {
  // define association here
  this.belongsTo(models.Vehicle); // This is to add a VehicleId attribute to Racer to hold the primary key value for Vehicle
}

Then in the racer's migration file (called something like 20210429015612-create-racer.js):

'use strict';
module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable('Racers', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      name: {
        type: Sequelize.STRING
      },
      nickname: {
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
    // And here's where the trick takes place:
    await queryInterface.addColumn(
      'Racer', // name of Source model
      'VehicleId', // name of the key we're adding 
      {
        type: Sequelize.INTEGER,
        references: {
          model: 'Vehicle', // name of Target model
          key: 'id', // key in Target model that we're referencing
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      }
    );
  },
  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable('Racers');
    await queryInterface.removeColumn(
      'Racers', // name of Source model
      'VehicleId' // key we want to remove
    );
  }
};

After doing this modification I had to run in console

node_modules/.bin/sequelize db:migrate:undo

to run the down function to drop the racers' table and then re-run

node_modules/.bin/sequelize db:migrate

And that's it, if you go to pdAdmin again and refresh the tables you will see the field VehicleId in the Racers table.

For further reference check out this guy's article.

Upvotes: 5

Related Questions