Sequelizejs与OtherKey属于ToMany关系

时间:2017-03-05 16:18:43

标签: node.js sequelize.js

我正在创建一个关于歌曲和艺术家的应用程序,以下是数据库架构:

歌有很多艺术家,而艺术家有很多歌,这是多对多的关系,所以我定义了一个连接表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使用SongsArtists主键('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"

2 个答案:

答案 0 :(得分:4)

您使用的otherKey选项不是用于选择关联中使用的源模型字段。根据{{​​1}}

的文档
  

连接表中的外键名称(表示目标模型)或表示另一列的类型定义的对象(有关语法,请参阅Sequelize.define)。使用对象时,可以添加name属性以设置列的名称。默认为目标的名称+目标

的主键

它为目标模型的连接表指定外键,因此它是连接表中的列,而不是源/目标表中的列。更重要的是,根据Sequelize belongsToMany的源代码,在这种关联中不可能使用除主键之外的其他字段。下面是一行代码,用于从源模型中选择字段

otherKey

const sourceKey = this.source.rawAttributes[this.source.primaryKeyAttribute]; 是您的源模型,而不是this.sourceSong。如您所见,它会自动选择此模型的Artist,在两种情况下都是primaryKeyAttribute字段。 在github上甚至存在与您遇到的相同问题有关的问题,答案是在这种情况下只有主键字段是可接受的 - Specify non-primary target key in belongsToMany association

答案 1 :(得分:0)

5.15.0中指出,在Sequelize this PR中添加了对多对多关系的targetKeysourceKey支持。

在此处引用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'
});