5

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.

Myles Smith
  • 51
  • 1
  • 3
  • Should sequelize creates the defaultVehicle column in Racer by default or must you declare it explicitily? – Guedes Jan 30 '21 at 20:03
  • @Guedes I tried explicitly creating the column in the model/migration and of course the columns where there but they didn't seem to be associated with the vehicle table. I was thinking maybe I need to call the associate method but it looks like sequelize is handling that automatically in the index file. – Myles Smith Jan 30 '21 at 20:11
  • @MylesSmith did you find the solution for your issue? I'm experiencing exactly the same. – andres_v Apr 29 '21 at 17:37

2 Answers2

5

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.

andres_v
  • 399
  • 1
  • 4
  • 12
  • This worked for me. The only thing I would like to point out is that in my case, I have a users table and a user_profiles table. When I tried adding the column to my users table, it would fail since my second migration file was for the user profile and when I was trying to reference it, it wasn't created yet. So I had to first create users table, then user_profiles table, then I added another migration file that only added the column after both tables were created. – Alexiz Hernandez Sep 13 '21 at 18:47
0

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)
}