我正在创建一个关于歌曲和艺术家的应用程序,以下是数据库架构:
歌有很多艺术家,而艺术家有很多歌,这是多对多的关系,所以我定义了一个连接表SongArtist
:
歌曲艺术家模特:
module.exports = function(sequelize, DataTypes) {
var SongArtist = sequelize.define('SongArtist', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
songId: {
type: DataTypes.INTEGER,
allowNull: false,
},
artistId: {
type: DataTypes.INTEGER,
allowNull: false,
}
}, {
tableName: 'SongArtist',
});
return SongArtist;
};
默认行为是SongArtist
使用Songs
和Artists
主键('id')来查询,但我想在歌曲和艺术家中使用neteaseId
列多对多查询,所以我在下面使用otherKey
:
歌曲模特:
module.exports = function(sequelize, DataTypes) {
var Songs = sequelize.define('Songs', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: true
},
neteaseId: {
type: DataTypes.INTEGER,
allowNull: false,
unque: true
}
}, {
tableName: 'Songs',
classMethods: {
associate: (models) => {
Songs.belongsToMany(models.Artists,{
through: 'SongArtist',
foreignKey: 'songId',
otherKey: 'neteaseId'
})
}
}
});
return Songs;
};
艺术家模特:
module.exports = function(sequelize, DataTypes) {
var Artists = sequelize.define('Artists', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: true
},
neteaseId: {
type: DataTypes.INTEGER,
allowNull: false,
unque: true
}
}, {
tableName: 'Artists',
classMethods: {
associate: (models) => {
Artists.belongsToMany(models.Songs,{
through: 'SongArtist',
foreignKey: 'artistId',
otherKey: 'neteaseId'
})
}
}
});
return Artists;
};
但是当我使用以下代码执行查询时,它会向我抛出错误:
models.Songs.findAll({include: [models.Artists, models.Album]})
> SequelizeDatabaseError: column Artists.SongArtist.neteaseId does not exist
那么如何在多对多查询中更改默认查询列,并应生成以下sql:
LEFT OUTER JOIN ("SongArtist" AS "Artists.SongArtist"
INNER JOIN "Artists" AS "Artists" ON "Artists"."neteaseId" = "Artists.SongArtist"."artistId")
ON "Songs"."id" = "Artists.songId"."songId"
而不是
LEFT OUTER JOIN ("SongArtist" AS "Artists.SongArtist"
INNER JOIN "Artists" AS "Artists" ON "Artists"."id" = "Artists.SongArtist"."artistId")
ON "Songs"."id" = "Artists.SongArtist"."songId"
答案 0 :(得分:4)
您使用的otherKey
选项不是用于选择关联中使用的源模型字段。根据{{1}}
连接表中的外键名称(表示目标模型)或表示另一列的类型定义的对象(有关语法,请参阅Sequelize.define)。使用对象时,可以添加name属性以设置列的名称。默认为目标的名称+目标
的主键
它为目标模型的连接表指定外键,因此它是连接表中的列,而不是源/目标表中的列。更重要的是,根据Sequelize belongsToMany
的源代码,在这种关联中不可能使用除主键之外的其他字段。下面是一行代码,用于从源模型中选择字段
otherKey
const sourceKey = this.source.rawAttributes[this.source.primaryKeyAttribute];
是您的源模型,而不是this.source
或Song
。如您所见,它会自动选择此模型的Artist
,在两种情况下都是primaryKeyAttribute
字段。
在github上甚至存在与您遇到的相同问题有关的问题,答案是在这种情况下只有主键字段是可接受的 - Specify non-primary target key in belongsToMany association
答案 1 :(得分:0)
在5.15.0中指出,在Sequelize this PR中添加了对多对多关系的targetKey
和sourceKey
支持。
在此处引用the docs。
有四种情况需要考虑:
我们可能希望使用默认的主要对象进行多对多关系 Foo和Bar键:
Foo.belongsToMany(Bar, { through: 'foo_bar' }); // This creates a junction table `foo_bar` with fields `fooId` and `barId`
对于Foo,我们可能希望使用默认主键>进行多对多的关系,而对于Bar,我们希望使用不同的字段:
Foo.belongsToMany(Bar, { through: 'foo_bar', targetKey: 'title' }); // This creates a junction table `foo_bar` with fields `fooId` and `barTitle`
我们可能希望使用Foo的不同字段和Bar的默认主键进行多对多关系:
Foo.belongsToMany(Bar, { through: 'foo_bar', sourceKey: 'name' }); // This creates a junction table `foo_bar` with fields `fooName` and `barId`
对于Foo和Bar,我们可能希望使用不同的字段进行多对多关系:
Foo.belongsToMany(Bar, { through: 'foo_bar', sourceKey: 'name', targetKey: 'title' }); // This creates a junction table `foo_bar` with fields `fooName` and `barTitle`
在this commit中显示的代码示例。粘贴到此处以快速参考。
const User = this.sequelize.define('User', {
id: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
field: 'user_id'
},
userSecondId: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
field: 'user_second_id'
}
}, {
tableName: 'tbl_user',
indexes: [
{
unique: true,
fields: ['user_second_id']
}
]
});
const Group = this.sequelize.define('Group', {
id: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
field: 'group_id'
},
groupSecondId: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
field: 'group_second_id'
}
}, {
tableName: 'tbl_group',
indexes: [
{
unique: true,
fields: ['group_second_id']
}
]
});
User.belongsToMany(Group, {
through: 'usergroups',
sourceKey: 'userSecondId'
});
Group.belongsToMany(User, {
through: 'usergroups',
sourceKey: 'groupSecondId'
});