Sequelize Model - 使用belongsToMany n:m关联删除项目

时间:2017-01-25 12:36:54

标签: node.js postgresql sequelize.js

我正在尝试为我的应用设置DELETE路由,该路由使用Sequelize和Postgres数据库。我有两个不同的模型,“产品”和“兴趣”(将兴趣视为一种类别或标签)。产品可以有多个兴趣和兴趣可以有多个产品,所以我使用belongsToMany关联。因此,我有三个表:ProductsInterestsProductInterests。以下是我设置关联的方法:

对于产品:

Product.belongsToMany(models.Interest, {
    foreignKey: 'productId',
    through: models.ProductInterest,
    as: 'interests',
    onDelete: 'CASCADE',
    hooks: true
});

兴趣:

Interest.belongsToMany(models.Product, {
    through: models.ProductInterest,
    as: 'products',
    foreignKey: 'interestId',
    onDelete: 'CASCADE',
    hooks: true
});

这是我的产品控制器中调用的方法:

destroy(req, res) {
    return Product
        .findById(req.params.productId, {
            include: [
                {
                    association: Product.associations.interests
                }
            ]
        })
        .then((product) => {
            if (!product) {
                return res.status(404).send({
                    message: 'Product not found'
                });
            }
            return product
                .destroy()
                // I've also tried the following:
                // .destroy({
                //    truncate: true,
                //    cascade: true
                // })
                .then(() => res.status(204).send())
                .catch(error => res.status(400).send(error));
        })
        .catch(error => res.status(400).send(error));

这会导致无限循环,并记录这些查询:

Executing (default): SELECT "Product"."id", "Product"."title", "Product"."brand", "Product"."url", "Product"."imageUrl", "Product"."currentPrice", "Product"."male", "Product"."female", "Product"."asin", "Product"."storeName", "Product"."createdAt", "Product"."updatedAt", "ProductInterest"."createdAt" AS "ProductInterest.createdAt", "ProductInterest"."updatedAt" AS "ProductInterest.updatedAt", "ProductInterest"."interestId" AS "ProductInterest.interestId", "ProductInterest"."productId" AS "ProductInterest.productId" FROM "Products" AS "Product" INNER JOIN "ProductInterests" AS "ProductInterest" ON "Product"."id" = "ProductInterest"."productId" AND "ProductInterest"."interestId" = 1;
Executing (default): SELECT "Interest"."id", "Interest"."name", "Interest"."createdAt", "Interest"."updatedAt", "ProductInterest"."createdAt" AS "ProductInterest.createdAt", "ProductInterest"."updatedAt" AS "ProductInterest.updatedAt", "ProductInterest"."interestId" AS "ProductInterest.interestId", "ProductInterest"."productId" AS "ProductInterest.productId" FROM "Interests" AS "Interest" INNER JOIN "ProductInterests" AS "ProductInterest" ON "Interest"."id" = "ProductInterest"."interestId" AND "ProductInterest"."productId" = 6;

我的意图是a)删除Product表中的产品; b)删除ProductInterest表中的关联记录。如果要删除产品,我不希望删除兴趣本身。同样,如果删除利息(仅关联),我不希望删除关联的产品。

我已经尝试了几种不同的排列,但找不到合适的方法。任何帮助将不胜感激。

更新

调试sequelize代码有点让我觉得我不应该像这样使用cascade。它基本上是试图删除产品,然后(由于级联)尝试删除兴趣(我不想要),(由于级联)然后尝试删除相关产品等等仍然不确定是什么正确的解决方案是。

2 个答案:

答案 0 :(得分:2)

首先,belongsToMany的默认行为是DOCS中写到的级联删除

  

对于1:1和1:m关联,默认选项是SET NULL用于删除,而CASCADE用于更新。对于n:m,两者的默认值为CASCADE。这意味着,如果您从n:m关联的一侧删除或更新一行,则联接表中引用该行的所有行也将被删除或更新。

但是我认为,只有在像这样在表创建或迁移中创建外键引用时显式编写它,它才会起作用:

queryInterface.createTable('ProductInterest', {
  productId: {
    type: Sequelize.INTEGER,
    references: {
        model: 'Product',
        key: '_id',
      },
    onDelete: 'cascade'
  },
  interestId: {
    type: Sequelize.INTEGER,
    references: {
        model: 'Interest',
        key: '_id',
      },
    onDelete: 'cascade'
  },
});

答案 1 :(得分:1)

据我了解,belongsToMany为给定的源和目标创建了一个连接表模型 - 因此,对于您的两个表ProductInterest, 执行Product.belongsToMany(Interest)会创建一个新表ProductInterest,它基本上只是一个ProductId / InterestId对表。

Sequelize的默认行为是,当删除ProductInterest时,ProductInterest中的条目也会被删除。简而言之,它听起来像默认情况下你想要的删除约束。我不确定将这些额外的onDelete约束传递给关联的效果是什么,但听起来你发现它们会导致一些意外的行为。