【问题标题】:Trying to create an instance and multiple related instances in a many to many relationship尝试以多对多关系创建一个实例和多个相关实例
【发布时间】:2017-09-11 05:52:06
【问题描述】:

我正在尝试使用联结表创建一个实例和多个具有多对多关系的相关实例。

在创建多个相关实例时,我还需要向联结表上的属性添加一个值。我不知道是否是我缺乏对续集或承诺的了解导致了我的问题。

我使用的代码如下。此代码确实将项目添加到数据库中,但我需要在操作完成后重定向,这不起作用。

基本上,我需要创建一个食谱。一旦创建,我需要创建成分并将它们与该食谱相关联。成分存储在来自 HTML 页面上的表单的数组中。在关联成分时,我需要将成分数量添加到 RecipeIngredients 表中,这是关系的直通部分(联结表)。

global.db.Recipe.belongsToMany(
    global.db.Ingredient, 
    { 
        as: 'Ingredients', 
        through: global.db.RecipeIngredients, 
        foreignKey: 'recipe_id' 
    });
global.db.Ingredient.belongsToMany(
    global.db.Recipe, 
    { 
        as: 'Recipes', 
        through: global.db.RecipeIngredients, 
        foreignKey: 'ingredient_id' 
    });

router.post('/new', ensureLoggedIn, bodyParser.json(), function (req, res) {
    var recipeName = req.body.recipe_name;
    var steps = req.body.steps;
    var ingredients = req.body.ingredients;
    var ingredientQty = {};
    var currentIngredient;
    var ingredientsToAdd = [];

    db.Recipe.create({
        recipe_name: recipeName,
        directions: steps,
        FamilyId: req.user.FamilyId,
        CreatedBy: req.user._id
    })
    .then(function (recipe) {
        for (var i = 0; i < ingredients.length; i++) {

            currentIngredient = ingredients[i];
            ingredientQty[currentIngredient.ingredient_name] = 
currentIngredient.quantity;

            db.Ingredient.findOrCreate({
                where: { 
                    ingredient_name: currentIngredient.ingredient_name, 
                    FamilyId: req.user.FamilyId 
                }
            })
            .spread(function (ingredient, created) {
                if (created) {
                    console.log("Added Ingredient to DB: " + 
                    currentIngredient.ingredient_name);
                }

            ingredient.Recipes = {
                ingredient_quantity: 
                    ingredientQty[ingredient.ingredient_name]
            };
            ingredient.CreatedBy = req.user._id;
            recipe.addIngredient(ingredient)
            .then(function () {
                console.log("Added Ingredient " + ingredient.ingredient_name 
                + " to Recipe " + recipe.recipe_name);
            });
        })
    }

})
.finally(function(recipe){
    res.redirect('/recipes');
});
});

任何帮助将不胜感激。我知道我因为尝试在循环中使用 Promise 而遇到问题,我只是不知道我还能如何做到这一点。

【问题讨论】:

    标签: node.js postgresql express sequelize.js


    【解决方案1】:

    我能够解决我遇到的问题。答案here 帮助我想出了我的解决方案。 我在下面发布解决方案,以防其他人遇到类似问题。我创建了一个变量来存储来自 Recipe.create() 的 Promise,我使用 Promise.map 从表单数据中查找或创建所有成分。因为findOrCreate 返回一个包含 Promise 的数组和一个布尔值(如果项目已创建),因此我必须从 Promise.map 函数的结果中获取实际成分。所以我使用 JavaScript array.map() 函数从数组中获取第一项。最后,再次使用 Promise.map 将每种成分添加到配方中

    var ingredients = req.body.ingredients,
        recipeName = req.body.recipeName,
        ingredientsQty = {}; // Used to map the ingredient and quantity for the 
                             // relationship, because of the Junction Table
    
    var recipe = models.Recipe.create({recipe_name: recipeName});
    
    // Use Promise.map to findOrCreate all ingredients from the form data
    Promise.map(ingredients, function(ing){
        ingredientsQty[ing.ingredient_name] = ing.ingredient_quantity;
        return models.Ingredient.findOrCreate({ where: { ingredient_name: ing.ingredient_name}});
    })
    
    // Use JavaScript's Array.prototype.map function to return the ingredient 
    // instance from the array returned by findOrCreate
    .then(function(results){
        return results.map(function(result){
            return result[0];
        });
    })
    
    // Return the promises for the new Recipe and Ingredients
    .then(function(ingredientsInDB){
        return Promise.all([recipe, ingredientsInDB]);
    })
    
    // Now I can use Promise.map again to create the relationship between the / 
    // Recipe and each Ingredient
    .spread(function(addedRecipe, ingredientsToAdd){
        recipe = addedRecipe;
        return Promise.map(ingredientsToAdd, function(ingredientToAdd){
            ingredientToAdd.RecipeIngredients = {
                ingredient_quantity: ingredientsQty[ingredientToAdd.ingredient_name]
            };
    
            return recipe.addIngredient(ingredientToAdd);
        });
    })
    
    // And here, everything is finished
    .then(function(recipeWithIngredients){
       res.end
    });
    

    【讨论】:

      【解决方案2】:

      使用 sequelize,您可以一步创建对象及其关联对象,假设您正在创建的所有对象都是新的。这也称为嵌套创建。请参阅this 链接并向下滚动到标题为“使用关联创建”的部分

      谈到您的问题,RecipeIngredient 之间存在多对多关系,RecipeIngredients 是联接表。

      假设您要创建一个新的 Recipe 对象,例如:

      var myRecipe = {
        recipe_name: 'MyRecipe',
        directions: 'Easy peasy',
        FamilyId: 'someId',
        CreatedBy: 'someUserId'
      }
      

      还有一个成分对象数组,例如:

      var myRecipeIngredients = [
        { ingredient_name: 'ABC', FamilyId: 'someId'},
        { ingredient_name: 'DEF', FamilyId: 'someId'},
        { ingredient_name: 'GHI', FamilyId: 'someId'}]
      
      // associate the 2 to create in 1 step
      myRecipe.Ingredients = myRecipeIngredients;
      

      现在,您可以一步创建myRecipe 及其关联的myRecipeIngredients,如下所示:

      Recipe.create(myRecipe, {include: {model: Ingredient}})
      .then(function(createdObjects){
         res.json(createdObjects);
      })
      .catch(function(err){
         next(err);
      });
      

      仅此而已!
      Sequelize 将在 Recipe 中创建 1 行,在 Ingredient 中创建 3 行,在 RecipeIngredients 中创建 3 行来关联它们。

      【讨论】:

      • 好的,这有帮助,但是如何使用这种方法更新 RecipeIngredients 上的字段?当食谱和配料相关时,我需要将数量添加到创建的 RecipeIngredients 行。数量存储在来自 req.body 的成分对象中
      • 哦,对不起。我完全忘记了这个要求。但是,我不得不承认,我自己还没有尝试过,但是如果您在“属于多方关联”部分下看到 documentation,他们提到: >要向用户添加新项目并且设置其状态,您将额外的 options.through 传递给设置器,其中包含连接表的属性。 user.addProject(project, { through: { status: 'started' }})希望这会有所帮助。
      • 我看过那个文档。我不明白的是如何添加所有成分并为它们添加设置值。该示例仅添加了一个项目。看起来我需要单独添加每种成分。这让我想到了我遇到的问题。不过感谢您的尝试。
      • models.project.create({ name: 'Sample' }) .then(function (project){ project.createTask({ name: 'Sample' }, { through: { extraColumnOnJoinTable: '123' } }) .then(res.json({ success: true })); }) 上面的代码不起作用。它创建一个项目,然后是一个任务,然后是 ProjectTasks(连接表)中的一条记录,但 没有 extraColumnOnJoinTable 的值。我不确定,但你可能想用 sequelize 打开一个问题。
      • 你认为使用 Promise.Map 可以解决这个问题吗?我对 promuses 还不是很了解,所以我不完全了解如何使用地图或它是否可以工作
      猜你喜欢
      • 2020-07-27
      • 2021-11-06
      • 2017-05-18
      • 1970-01-01
      • 2020-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多