多对多关联

多对多关联用于将源与多个目标相连接. 此外,目标也可以连接到多个源.

  1. Project.belongsToMany(User, {through: 'UserProject'});
  2. User.belongsToMany(Project, {through: 'UserProject'});

这将创建一个名为 UserProject 的新模型,具有等效的外键projectIduserId. 属性是否为camelcase取决于由表(在这种情况下为UserProject)连接的两个模型.

定义 throughrequired. Sequelize 以前会尝试自动生成名称,但并不总是导致最合乎逻辑的设置.

这将添加方法 getUsers, setUsers, addUser,addUsersProject, 还有 getProjects, setProjects, addProject, 和 addProjectsUser.

有时,你可能需要在关联中使用它们时重命名模型. 让我们通过使用别名(as)选项将 users 定义为 workers 而 projects 定义为t asks. 我们还将手动定义要使用的外键:

  1. User.belongsToMany(Project, { as: 'Tasks', through: 'worker_tasks', foreignKey: 'userId' })
  2. Project.belongsToMany(User, { as: 'Workers', through: 'worker_tasks', foreignKey: 'projectId' })

foreignKey 将允许你在 through 关系中设置 source model 键.

otherKey 将允许你在 through 关系中设置 target model 键.

  1. User.belongsToMany(Project, { as: 'Tasks', through: 'worker_tasks', foreignKey: 'userId', otherKey: 'projectId'})

当然你也可以使用 belongsToMany 定义自我引用:

  1. Person.belongsToMany(Person, { as: 'Children', through: 'PersonChildren' })
  2. // 这将创建存储对象的 ID 的表 PersonChildren.

来源(Source)键 和 目标(Target)键

如果要创建不使用默认主键的 belongsToMany 关系,则需要进行一些设置工作. 你必须为 belongsToMany 的两端设置恰当的 sourceKey (targetKey 可选).此外,你还必须确保在关系中创建适合的索引.例如:

  1. const User = this.sequelize.define('User', {
  2. id: {
  3. type: DataTypes.UUID,
  4. allowNull: false,
  5. primaryKey: true,
  6. defaultValue: DataTypes.UUIDV4,
  7. field: 'user_id'
  8. },
  9. userSecondId: {
  10. type: DataTypes.UUID,
  11. allowNull: false,
  12. defaultValue: DataTypes.UUIDV4,
  13. field: 'user_second_id'
  14. }
  15. }, {
  16. tableName: 'tbl_user',
  17. indexes: [
  18. {
  19. unique: true,
  20. fields: ['user_second_id']
  21. }
  22. ]
  23. });
  24. const Group = this.sequelize.define('Group', {
  25. id: {
  26. type: DataTypes.UUID,
  27. allowNull: false,
  28. primaryKey: true,
  29. defaultValue: DataTypes.UUIDV4,
  30. field: 'group_id'
  31. },
  32. groupSecondId: {
  33. type: DataTypes.UUID,
  34. allowNull: false,
  35. defaultValue: DataTypes.UUIDV4,
  36. field: 'group_second_id'
  37. }
  38. }, {
  39. tableName: 'tbl_group',
  40. indexes: [
  41. {
  42. unique: true,
  43. fields: ['group_second_id']
  44. }
  45. ]
  46. });
  47. User.belongsToMany(Group, {
  48. through: 'usergroups',
  49. sourceKey: 'userSecondId'
  50. });
  51. Group.belongsToMany(User, {
  52. through: 'usergroups',
  53. sourceKey: 'groupSecondId'
  54. });

如果你想要连接表中的其他属性,则可以在定义关联之前为连接表定义一个模型,然后再说明它应该使用该模型进行连接,而不是创建一个新的关联:

  1. class User extends Model {}
  2. User.init({}, { sequelize, modelName: 'user' })
  3. class Project extends Model {}
  4. Project.init({}, { sequelize, modelName: 'project' })
  5. class UserProjects extends Model {}
  6. UserProjects.init({
  7. status: DataTypes.STRING
  8. }, { sequelize, modelName: 'userProjects' })
  9. User.belongsToMany(Project, { through: UserProjects })
  10. Project.belongsToMany(User, { through: UserProjects })

要向 user 添加一个新 project 并设置其状态,你可以将额外的 options.through 传递给 setter,其中包含连接表的属性

  1. user.addProject(project, { through: { status: 'started' }})

默认情况下,上面的代码会将 projectId 和 userId 添加到 UserProjects 表中, 删除任何先前定义的主键属性 - 表将由两个表的键的组合唯一标识,并且没有其他主键列. 要在 UserProjects 模型上强添加一个主键,你可以手动添加它.

  1. class UserProjects extends Model {}
  2. UserProjects.init({
  3. id: {
  4. type: Sequelize.INTEGER,
  5. primaryKey: true,
  6. autoIncrement: true
  7. },
  8. status: DataTypes.STRING
  9. }, { sequelize, modelName: 'userProjects' })

使用多对多你可以基于 through 关系查询并选择特定属性. 例如通过 through 使用findAll

  1. User.findAll({
  2. include: [{
  3. model: Project,
  4. through: {
  5. attributes: ['createdAt', 'startedAt', 'finishedAt'],
  6. where: {completed: true}
  7. }
  8. }]
  9. });

当通过模型不存在主键时,Belongs-To-Many会创建唯一键. 可以使用 uniqueKey 选项覆盖此唯一键名.

  1. Project.belongsToMany(User, { through: UserProjects, uniqueKey: 'my_custom_unique' })