Sequelize多态多对多 - `add` mixin不起作用

Posted

技术标签:

【中文标题】Sequelize多态多对多 - `add` mixin不起作用【英文标题】:Sequelize polymorphic many to many - `add` mixin not working 【发布时间】:2022-01-23 02:14:58 【问题描述】:

我有一个用于客户调查的多对多多态关联设置。我遇到的问题是在Survey 的模型实例上使用add mixin 时。如果连接表已经有一个项目,其 surveyed 字段等于新可调查的 id,它将被覆盖。

survey表:

id name
1 'Customer Survey'

scheduled_sessions表:

id appointment_data
10 "someData" : []

service_provider表:

id name
10 Joe Doe

survey_surveyable表:

survey surveyable surveyed
1 serviceProvider 10

当我add 一个计划会话恰好与服务提供者具有相同的 id 时,连接表行被覆盖:

const surveyInstance = await DB.Survey.findByPk(1);
const scheduledSessionInstance = await DB.ScheduledSession.findByPk(10);

surveyInstance.addScheduledSession(
  scheduledSessionInstance,
   through:  surveyable: "scheduledSession"  
);

return surveyInstance.save();

这是后续运行的 SQL 查询:

SELECT "id", "name"
  FROM "surveys" AS "Survey"
  WHERE "Survey"."id" = 1;

SELECT "id", "appointment_data" AS "appointmentData"
  FROM "scheduled_sessions" AS "ScheduledSession"
  WHERE "ScheduledSession"."id" = 10;

SELECT "survey", "surveyable", "surveyed"
  FROM "survey_surveyable" AS "SurveySurveyable"
  WHERE
    "SurveySurveyable"."survey" = 1 AND
    "SurveySurveyable"."surveyed" IN (10);

UPDATE "survey_surveyable"
  SET "surveyable"=$1
  WHERE
    "survey" = $2 AND
    "surveyed" = $3

由于计划会话和服务提供者都有id=10,连接表中的服务提供者行被覆盖导致:

survey_surveyable表:

survey surveyable surveyed
1 scheduledSession 10

它应该在哪里:

survey_surveyable表:

survey surveyable surveyed
1 serviceProvider 10
1 scheduledSession 10

这是一个后续问题,还是我错误地使用了add mixin?

我的模型:

Survey.js:

module.exports = (sequelize, DataTypes) => 
  class Survey extends sequelize.Sequelize.Model ;

  Survey.init(
    
      id: 
        type: DataTypes.INTEGER,
        autoIncrement: true,
        allowNull: false,
        primaryKey: true
      ,
      name: 
        type: DataTypes.STRING,
        allowNull: false
      
    ,
    
      timestamps: false,
      tableName: "surveys",
      sequelize
    
  );

  Survey.associate = (models) => 
    Survey.belongsToMany(models.ScheduledSession, 
      through: 
        model: models.SurveySurveyable,
        unique: false
      ,
      foreignKey: "survey",
      constraints: false
    );
  ;

  return Survey;
;

ScheduledSession.js:

module.exports = (sequelize, DataTypes) => 
  class ScheduledSession extends sequelize.Sequelize.Model ;

  ScheduledSession.init(
    
      id: 
        type: DataTypes.INTEGER,
        autoIncrement: true,
        allowNull: false,
        primaryKey: true
      
      appointmentData: 
        type: DataTypes.JSONB,
        allowNull: false,
        field: "appointment_data"
      
    ,
    
      paranoid: true,
      tableName: "scheduled_sessions",
      sequelize
    
  );

  ScheduledSession.associate = (models) => 
    ScheduledSession.belongsToMany(models.Survey, 
      through: 
        model: models.SurveySurveyable,
        unique: false,
        scope: 
          surveyable: "scheduledSession"
        
      ,
      foreignKey: "surveyed",
      constraints: false
    );
  ;

  return ScheduledSession;
;

ServiceProvider.js:

module.exports = (sequelize, DataTypes) => 
  class ServiceProvider extends sequelize.Sequelize.Model ;

  ServiceProvider.init(
    
      id: 
        type: DataTypes.INTEGER,
        autoIncrement: true,
        allowNull: false,
        primaryKey: true
      
      name: 
        type: DataTypes.STRING,
        allowNull: false
      
    ,
    
      paranoid: true,
      tableName: "service_provider",
      sequelize
    
  );

  ServiceProvider.associate = (models) => 
    ServiceProvider.belongsToMany(models.Survey, 
      through: 
        model: models.SurveySurveyable,
        unique: false,
        scope: 
          surveyable: "serviceProvider"
        
      ,
      foreignKey: "surveyed",
      constraints: false
    );
  ;

  return ServiceProvider;

SurveySurveyable.js:

module.exports = (sequelize, DataTypes) => 
  class SurveySurveyable extends sequelize.Sequelize.Model ;

  SurveySurveyable.init(
    
      survey: 
        type: DataTypes.INTEGER,
        allowNull: false,
        primaryKey: true
      ,
      surveyable: 
        type: DataTypes.STRING,
        allowNull: false,
        primaryKey: true
      ,
      surveyed: 
        type: DataTypes.INTEGER,
        allowNull: false,
        primaryKey: true,
        constraints: false
      
    ,
    
      timestamps: false,
      tableName: "survey_surveyable",
      sequelize,
      freezeTableName: true
    
  );

  return SurveySurveyable;
;

【问题讨论】:

【参考方案1】:

您正在使用 Survey 的 mixin,但在 Survey 的关联中缺少 scope

Survey.associate = (models) => 
  Survey.belongsToMany(models.ScheduledSession, 
    through: 
      model: models.SurveySurveyable,
      unique: false,
      scope:                            // This is missing
        surveyable: "scheduledSession"
      
    ,
    foreignKey: "survey",
    constraints: false
  );
;

【讨论】:

你是我的英雄!由于某种原因,sequelize 文档中缺少此内容。

以上是关于Sequelize多态多对多 - `add` mixin不起作用的主要内容,如果未能解决你的问题,请参考以下文章

如何查询多对多关系sequelize?

在 Sequelize.js 中为多对多关系添加默认角色

如何在 Sequelize 中按多对多关系排序?

Sequelize 查询在多对多关系中显示附加数据

Sequelize 多对多关系同一张表

Sequelize:多对多表(CROSS TABLE)关联到另一个表