Я создаю базу данных рецептов (обычно известную как кулинарная книга), где мне нужно иметь отношение "многие ко многим" между ингредиентами и рецептами, и я использую в сочетании с .

Когда ингредиент добавляется к рецепту, мне нужно объявить правильное количество этого ингредиента, который попадает в рецепт.

Я заявил (сокращенный пример)

var Ingredient = sequelize.define('Ingredient', {
    name: Sequelize.STRING
}, {
    freezeTable: true
});

var Recipe = sequelize.define('Recipe', {
    name: Sequelize.STRING
}, {
    freezeTable: true
});

var RecipeIngredient = sequelize.define('RecipeIngredient', {
    amount: Sequelize.DOUBLE
});

Ingredient.belongsToMany(Recipe, { through: RecipeIngredient });
Recipe.belongsToMany(Ingredient, {
    through: RecipeIngredient,
    as: 'ingredients'
});

Моя проблема состоит в том, как данные возвращаются, когда одна конечные точки моей остальной части делают

router.get('/recipes', function(req, res) {
    Recipe.findAll({
        include: [{
            model: Ingredient,
            as: 'ingredients'
         }]
    }).then(function(r) {
        return res.status(200).json(r[0].toJSON());
    })
});

В результате JSON, который отправляется клиенту, выглядит следующим образом (временные метки опущены):

{
  "id": 1,
  "name": "Carrots",
  "ingredients": [
    {
      "id": 1,
      "name": "carrot",
      "RecipeIngredient": {
        "amount": 12,
        "RecipeId": 1,
        "IngredientId": 1
      }
    }
  ]
}

Пока все, что я хотел, было

{
  "id": 1,
  "name": "Carrots",
  "ingredients": [
    {
      "id": 1,
      "name": "carrot",
      "amount": 12,
    }
  ]
}

То есть я хочу, чтобы в результат было включено поле amount из таблицы отношений, а не весь объект RecipeIngredient.

База данных, сгенерированная секвелогом, выглядит следующим образом:

Ingredients
id  name
1   carrot

Recipes
id  name
1   Carrots

RecipeIngredients
amount  RecipeId  IngredientId
12      1         1

Я попытался предоставить массив attributes как свойство для include следующим образом:

include: [{
    model: Ingredient,
    as: 'ingredients',
    attributes: []
 }]

Но установка {{x0}}, либо {{x1}}, так как значение атрибутов - значение, как ошибки, как

Необработанное отклонение SequelizeDatabaseError: ингредиенты столбца.RecipeIngredient.amount не существует

Очевидно, что я могу исправить это в JS, используя {{x0}}, но, конечно, должен быть способ сделать Sequelize сделать для меня работать?

0
ivarni 28 Дек 2015 в 19:25

3 ответа

Лучший ответ

Вы можете использовать Sequelize.liereral. Используя псевдоним ингредиента рецепта, вы можете написать следующим образом. Я не знаю, если это правильно. :)

[sequelize.literal('`TheAlias->RecipeIngredient`.amount'), 'amount'],

Я тестировал с sqlite3. Полученный результат с псевдонимом "ir"

{ id: 1,
  name: 'Carrots',
  created_at: 2018-03-18T04:00:54.478Z,
  updated_at: 2018-03-18T04:00:54.478Z,
  ir: [ { amount: 10, RecipeIngredient: [Object] } ] }

Смотрите полный код здесь.

https://github.com/eseom/sequelize-cookbook

1
eseom 18 Мар 2018 в 04:07

Я просмотрел документацию, но не смог найти ничего, что могло бы позволить мне объединить атрибуты объединенной таблицы в результат, так что похоже, что я застрял в чем-то вроде этого:

router.get('/recipes', function(req, res) {
    Recipe.findAll({
        include: [{
            model: Ingredient,
            as: 'ingredients',
            through: {
                attributes: ['amount']
            }
         }]
    }).then(function(recipes) {
        return recipes[0].toJSON();
    }).then(function(recipe) {
        recipe.ingredients = recipe.ingredients.map(function(i) {
            i.amount = i.RecipeIngredient.amount;
            delete i.RecipeIngredient;
            return i;
        });
        return recipe;
    }).then(function(recipe) {
        return res.status(200).json(recipe);
    });
});

Прохождение {{x0}} to {{x1}} позволяет мне отфильтровать, какие атрибуты я хочу включить из присоединения, но для жизни меня я не мог найти способ сделать Sequelize объединить его для меня.

Приведенный выше код вернет результат, который я хотел, но с дополнительными накладными расходами на цикл по списку ингредиентов, что не совсем то, что я хотел, но, если кто-то не предложит лучшее решение, я не вижу другого способа сделать это.

0
ivarni 30 Дек 2015 в 12:02

Я опаздываю до этого, но я вижу, что это было немного немного немного, так что вот мой ответ о том, как объединить атрибуты

Несколько случайных примеров в этом

router.get('/recipes', function(req, res) {
Recipe.findAll({
    include: [{
        model: Ingredient,
        as: 'ingredients',
        through: {
            attributes: ['amount']
        }
     }]
})
.then(docs =>{
    const response = {
        Deal: docs.map(doc =>{
            return{
                cakeRecipe:doc.recipe1,
                CokkieRecipe:doc.recipe2,
                Apples:doc.ingredients.recipe1ingredient
                spices:[
                    {
                     sugar:doc.ingredients.spice1,
                     salt:doc.ingredients.spice2
                    }
                ]

            }
        })
    }

})
res.status(200).json(response)

})

1
Evander Francis 17 Дек 2019 в 16:45