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找不到对应的字段,抛出错误。
修复步骤
- 修改AccessList模型的关联配置,显式指定targetKey为
RoleList的主键name:
// permission-model.js static associate(models) { AccessList.belongsToMany(models.RoleList, { through: models.RolePermission, foreignKey: 'access_id', targetKey: 'name' // 新增:指定目标模型的关联字段为name }); }
- 修改RoleList模型的关联配置,显式指定sourceKey为自身主键
name(可选,但能避免潜在的主键识别问题):
// role-model.js static associate(models) { RoleList.belongsToMany(models.AccessList, { through: models.RolePermission, foreignKey: 'role_name', sourceKey: 'name' // 新增:指定当前模型的关联字段为name }); }
- 修正RolePermission模型的id字段问题:迁移文件中
RolePermissions表定义了自增id主键,但模型中通过RolePermission.removeAttribute('id')移除了该字段,导致模型与数据库表结构不一致,需删除该行代码:
// role-permission-model.js // 删除这一行:RolePermission.removeAttribute('id');
完成以上修改后,重启Node服务即可解决报错。
内容的提问来源于stack exchange,提问作者MogerCS
相关产品推荐
相关产品推荐

