Sequelize中的关联功能未按预期工作

5

我将尝试输出一个嵌套关系,其中:

Cat拥有多个legs

Leg属于一个cat

Leg拥有一个paw

paw拥有多个leg

这是我的Cat模型:

module.exports = (sequelize, DataTypes) => {
  const Cat = sequelize.define('Cat', {
    userId: {
      type: DataTypes.STRING,
    },
  }, {});

  Cat.associate = function (models) {
    Cat.hasMany(models.Leg, {
      foreignKey: 'catId',
      as: 'legs',
    });
  };
  return Cat;
};

我的腿部模型:

module.exports = (sequelize, DataTypes) => {
  const Leg = sequelize.define('Leg', {
    originalValue: DataTypes.JSON,
    newValue: DataTypes.JSON,
    legId: DataTypes.INTEGER,
    objectId: DataTypes.INTEGER,
    pawId: DataTypes.INTEGER,
  }, {});

  Leg.associate = function (models) {
    Leg.belongsTo(models.Cat, {
      foreignKey: 'LegId',
      onDelete: 'CASCADE',
    });
    Leg.hasOne(models.Paw, {
      foreignKey: 'pawId',
    });
  };
  return Leg;
};

这是我的 Paw 模型。
module.exports = (sequelize, DataTypes) => {
  const Paw = sequelize.define('Paw', {
    pawType: DataTypes.STRING,
  }, {});
  Paw.associate = function (models) {
    Paw.hasMany(models.Leg, {
      foreignKey: 'pawId',
      as: 'paws',
    });
  };
  return Paw;
};

目前,当我查询 Cat 表时,我的代码输出以下内容:

[
    {
        "id": 1,
        "userId": "2wdfs",
        "createdAt": "2018-04-14T20:12:47.112Z",
        "updatedAt": "2018-04-14T20:12:47.112Z",
        "legs": [
            {
                "id": 1,
                "catId": 1,
                "pawId": 1,
                "createdAt": "2018-04-14T20:12:54.500Z",
                "updatedAt": "2018-04-14T20:12:54.500Z"
            }
        ]
    }
]

我希望在列出猫表中的所有内容时,也能包含来自爪子表的pawType。更准确地说,我需要做到这一点:

[
    {
        "id": 1,
        "userId": "2wdfs",
        "createdAt": "2018-04-14T20:12:47.112Z",
        "updatedAt": "2018-04-14T20:12:47.112Z",
        "legs": [
            {
                "id": 1,
                "catId": 1,
                "paws" : [
                   {
                    "id": 1,
                    "pawType": "cute"
                   }
                ]
                "createdAt": "2018-04-14T20:12:54.500Z",
                "updatedAt": "2018-04-14T20:12:54.500Z"
            }
        ]
    }
]

此外,这是我用来检索猫的查询语句。
return Cat.findAll({ include: [{ model: Leg, as: 'legs',include [{model: Paw,}], }], })

这是返回的错误信息:
{ SequelizeDatabaseError: column legs->Paw.pawId does not exist
{ error: column legs->Paw.pawId does not exist

完整的SQL命令

   sql: 'SELECT "Cat"."id", "Cat"."userId", "Cat"."createdAt", "Cat"."updatedAt", "legs"."id" AS "legs.id", "legs"."originalValue" AS "legs.originalValue", "legs"."newValue" AS "legs.newValue", "legs"."catId" AS "legs.catId", "legs"."objectId" AS "legs.objectId", "legs"."pawId" AS "legs.pawId", "legs"."createdAt" AS "legs.createdAt", "legs"."updatedAt" AS "legs.updatedAt", "legs->Paw"."id" AS "legs.Paw.id", "legs->Paw"."paw" AS "legs.Paw.paw", "legs->Paw"."pawId" AS "legs.Paw.pawId", "legs->Paw"."createdAt" AS "legs.Paw.createdAt", "legs->Paw"."updatedAt" AS "legs.Paw.updatedAt" FROM "Cats" AS "Cat" LEFT OUTER JOIN "Legs" AS "legs" ON "Cat"."id" = "legs"."catId" LEFT OUTER JOIN "Paws" AS "legs->Paw" ON "legs"."id" = "legs->Paw"."pawId";' },

此外,这是我用来检索猫的查询语句。 return Cat.findAll({ include: [{ model: Leg, as: 'legs', }], }) - sar agnew
1个回答

4

有许多问题。我将逐步解决这些问题。

1) 模型 默认情况下,如果你没有声明 primaryKey,sequelize 会自动为你添加一个 id 列。因此,legId 并不是一个有用的列。

此外,如果你关联一个模型,foreignKey 引用会被自动添加,因此不需要声明 pawId

因此,Legs.js 应该被修改为:

module.exports = (sequelize, DataTypes) => {
  var Leg = sequelize.define('Leg', {
    originalValue: DataTypes.JSON,
    newValue: DataTypes.JSON,
    objectId: DataTypes.INTEGER // not entirely sure what this is 
  })
  Leg.associate = function (models) {
    // associations
  }
  return Leg
}

以下是在 pgAdmin 中得到的列: enter image description here 2) 关联 以下关联没有意义,而且应该会导致错误:
Leg.hasOne(Paw)
Paw.hasMany(Leg)

Unhandled rejection Error: Cyclic dependency found. Legs is dependent of itself.
Dependency chain: Legs -> Paws => Legs

每个Leg(腿)应该只有一个Paw(爪子),建议采用以下方式:
Leg.associate = function (models) {
  // Leg.belongsTo(models.Cat)
  Leg.hasOne(models.Paw, {
    foreignKey: 'pawId',
    as: 'paw'
  })
}

Paw.associate = function (models) {
  Paw.belongsTo(models.Leg, {
    as: 'leg' // note this changed to make more sense
    foreignKey: 'pawId'
  })
}

3) 外键

Leg.belongsTo(models.Cat, {
  foreignKey: 'catId', // this should match
  onDelete: 'CASCADE'
})

Cat.hasMany(models.Leg, {
  foreignKey: 'catId', // this should match
  as: 'legs'
})

4) 预加载

当预加载嵌套关联时,您需要使用include来加载它们。您还应该使用与您的模型关联匹配的as别名:

Cat.findAll({
  include: [{
    model: Leg,
    as: 'legs', // Cat.legs 
    include: [{
      model: Paw,
      as: 'paw' // Leg.paw instead of Leg.pawId
    }]
  }]
})

使用整个设置和上面的查询,我获得:

[
  {
    "id": 1,
    "userId": "1",
    "createdAt": "2018-04-15T11:22:59.888Z",
    "updatedAt": "2018-04-15T11:22:59.888Z",
    "legs": [
      {
        "id": 1,
        "originalValue": null,
        "newValue": null,
        "objectId": null,
        "createdAt": "2018-04-15T11:22:59.901Z",
        "updatedAt": "2018-04-15T11:22:59.901Z",
        "catId": 1,
        "paw": {
          "id": 1,
          "pawType": null,
          "createdAt": "2018-04-15T11:22:59.906Z",
          "updatedAt": "2018-04-15T11:22:59.906Z",
          "pawId": 1
        }
      }
    ]
  }
]

额外

因为这显然是一个实践设置,你可以修改Paw成为belongsToMany关系(也许你有通过爪子连接的猫?),如下:

Paw.associate = function (models) {
  Paw.belongsToMany(models.Leg, {
    foreignKey: 'pawId',
    through: 'PawLegs  // a through join table MUST be defined
  })
}

这将是实现您最初尝试的方法的正确方式。
Leg.hasOne(paw)
paw.hasMany(leg)

我尝试了这个,但是它返回了一个错误。我更新了我的问题,包括查询和返回的错误。提前感谢您。 - sar agnew
1
我对我的回答进行了大量更新。在将来,不要立即根据答案更新您的问题。相反,请使用评论部分,并让回答者更新他们的答案。仅根据直接针对问题发表的评论或作为最后手段(例如回答用户变得无响应)更新问题。这是因为完全修改问题会使现有答案无效。 - vapurrmaid
完全没有问题。很抱歉有额外的评论,只是想帮助解释网站的工作方式(我所说的大部分内容都可以在帮助中心找到)。如果您每个“腿”只添加一个爪子id,那么您就拥有了一对一的关系。使用hasOnebelongsTo。否则,很遗憾我并不完全理解问题。 - vapurrmaid
1
啊,好的,太感谢你的帮助了! - sar agnew
没问题。只要注意,如果您删除了该列中引用的具有idPaw,则还必须在那里将其删除(因此这样做不太实际)。感谢您对我的回答进行了投票,如果您觉得它有用,请也为答案投票。希望一切都有所帮助。 - vapurrmaid
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接