You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS中Sequelize多对多belongsToMany关联报错求助

Sequelize多对多关联启动报错:TypeError: Cannot read property 'field' of undefined

错误栈信息

C:\Users\chand\Work\AssetOne\AI-app\ai-application-service\node_modules\sequelize\src\associations\belongs-to-many.js:136
      this.targetKeyField = this.target.rawAttributes[this.targetKey].field || this.targetKey;        
                                                                      ^
TypeError: Cannot read property 'field' of undefined
    at new BelongsToMany (C:\Users\chand\Work\AssetOne\AI-app\ai-application-service\node_modules\sequelize\src\associations\belongs-to-many.js:136:71)
    at Function.belongsToMany (C:\Users\chand\Work\AssetOne\AI-app\ai-application-service\node_modules\sequelize\src\associations\mixin.js:64:25)
    at Function.associate (C:\Users\chand\Work\AssetOne\AI-app\ai-application-service\models\access-list.js:13:18)
    at C:\Users\chand\Work\AssetOne\AI-app\ai-application-service\models\index.js:36:19
    at Array.forEach (<anonymous>)
    at Object.<anonymous> (C:\Users\chand\Work\AssetOne\AI-app\ai-application-service\models\index.js:34:17)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12)

使用的Sequelize版本

"sequelize-cli": "^6.6.0", 
"sequelize": "^6.31.0",

相关代码文件

index.js

'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.js')[env];
const db = {};

let sequelize;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable], {
    ...config,
    dialectModule: require('mysql2')
  });
} else {
  sequelize = new Sequelize(config.database, config.username, config.password, {
    ...config,
    dialectModule: require('mysql2')
  });
}

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

Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db); // 报错位置
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db

role-list-migration.js

'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable('RoleLists', {
      name: {
        allowNull: false,
        primaryKey: true,
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  async down(queryInterface, Sequelize) {
    await queryInterface.dropTable('RoleLists');
  }
};

permission-migration.js

'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable('AccessLists', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      access: {
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  async down(queryInterface, Sequelize) {
    await queryInterface.dropTable('AccessLists');
  }
};

role-permissions-migrations.js

'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable('RolePermissions', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      role_name: {
        type: Sequelize.STRING,
        onDelete: 'CASCADE',
        references: {
          model: "RoleLists",
          key: "name",
          as: 'role_name'
        }
      },
      access_id: {
        type: Sequelize.INTEGER,
        onDelete: 'CASCADE',
        references: {
          model: "AccessLists",
          key: "id"
        }
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  async down(queryInterface, Sequelize) {
    await queryInterface.dropTable('RolePermissions');
  }
};

role-model.js

'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class RoleList extends Model {
    static associate(models) {      
      RoleList.belongsToMany(models.AccessList, {
        through: models.RolePermission,
        foreignKey: 'role_name',
      });   
    }
  }
  RoleList.init({
    name: DataTypes.STRING,
  }, {
    sequelize,
    modelName: 'RoleList',
    tableName: 'RoleLists',
  });
  RoleList.removeAttribute('id');
  return RoleList;
};

permission-model.js

'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class AccessList 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) {
      AccessList.belongsToMany(models.RoleList, {
        through: models.RolePermission,
        foreignKey: 'access_id'
      });
    }
  }
  AccessList.init({
    access: DataTypes.STRING,
  }, {
    sequelize,
    modelName: 'AccessList',
    tableName: 'AccessLists',
  });
  return AccessList;
};

role-permission-model.js

'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class RolePermission 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) {
    }
  }
  RolePermission.init({
    role_name: {
        type: DataTypes.STRING,
        onDelete: 'CASCADE',
        references: {
          model: "RoleLists",
          key: "name",
          as: 'role_name'
        }
      },
      access_id: {
        type: DataTypes.INTEGER,
        onDelete: 'CASCADE',
        references: {
          model: "AccessLists",
          key: "id"
        }
      },
  }, {
    sequelize,
    modelName: 'RolePermission',
    tableName: 'RolePermissions',
  });
  RolePermission.removeAttribute('id');
  return RolePermission;
};

解决方案

问题根源

报错原因是AccessList关联RoleList时,Sequelize默认会查找目标模型(RoleList)的id字段作为关联的targetKey,但RoleList已经通过RoleList.removeAttribute('id')移除了该字段,且主键是自定义的name,导致Sequelize找不到对应的字段,抛出错误。

修复步骤

  1. 修改AccessList模型的关联配置,显式指定targetKey为RoleList的主键name:
// permission-model.js
static associate(models) {
  AccessList.belongsToMany(models.RoleList, {
    through: models.RolePermission,
    foreignKey: 'access_id',
    targetKey: 'name' // 新增:指定目标模型的关联字段为name
  });
}
  1. 修改RoleList模型的关联配置,显式指定sourceKey为自身主键name(可选,但能避免潜在的主键识别问题):
// role-model.js
static associate(models) {      
  RoleList.belongsToMany(models.AccessList, {
    through: models.RolePermission,
    foreignKey: 'role_name',
    sourceKey: 'name' // 新增:指定当前模型的关联字段为name
  });   
}
  1. 修正RolePermission模型的id字段问题:迁移文件中RolePermissions表定义了自增id主键,但模型中通过RolePermission.removeAttribute('id')移除了该字段,导致模型与数据库表结构不一致,需删除该行代码:
// role-permission-model.js
// 删除这一行:RolePermission.removeAttribute('id');

完成以上修改后,重启Node服务即可解决报错。

内容的提问来源于stack exchange,提问作者MogerCS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 03:32:35