Home > database >  What is the best way to have an index action return all records, and all record's foreign key a
What is the best way to have an index action return all records, and all record's foreign key a

Time:03-18

I have a recipes_controller that has an index action that fetches all the recipes in my database.

The problem is each recipe has_many ingredients and has_many recipe_ingredients.

The schema looks like:

  create_table "recipes", force: :cascade do |t|
    t.string "name"
    t.string "genre"
    t.bigint "user_id"
    t.index ["user_id"], name: "index_recipes_on_user_id"
  end

  create_table "ingredients", force: :cascade do |t|
    t.string "name"
    t.string "food_group"
  end

  create_table "recipe_ingredients", force: :cascade do |t|
    t.bigint "recipe_id"
    t.bigint "ingredient_id"
    t.integer "quantity"
    t.string "measurement_unit"
    t.index ["ingredient_id"], name: "index_recipe_ingredients_on_ingredient_id"
    t.index ["recipe_id"], name: "index_recipe_ingredients_on_recipe_id"
  end

When the index action for recipes_controller is called I would like to return a hash with all of the associations, eg:

{<Recipe id: 1, name: "rice", genre: "staples", user_id: 1, ingredients: [...ingredients], recipe_ingredients: [...recipe_ingredients]>, <Recipe id: 2, name: "toast", genre: "breakfast", user_id: 2, ingredients: [...ingredients], recipe_ingredients: [...recipe_ingredients]>}

I can essentially get this in SQL with

SELECT  recipes.*, 
        ingredients.name, 
        recipe_ingredients.quantity, 
        recipe_ingredients.measurement_unit 
        
FROM recipes
JOIN recipe_ingredients ON recipe_ingredients.recipe_id = recipes.id
JOIN ingredients on recipe_ingredients.ingredient_id = ingredients.id

But I'm struggling to understand how I can condense each recipe to only include it's discrete associations like in the example above, as one object that I can then display easily on the front end.

It may be important to know that the recipe_ingredients table is a join model

so my recipe and ingredient models look like:

class Recipe < ApplicationRecord
  belongs_to :user
  has_many :recipe_ingredients
  has_many :ingredients
end


class Ingredient < ApplicationRecord
  has_many :recipe_ingredients
  has_many :recipes, through: :recipe_ingredients
end

CodePudding user response:

The usual way to do this is use preloading, you don't have to worry about it being in a single object.

@recipes = Recipe.includes(:ingredients).where(...).order(...)

In view

@recipes.each do |recipe|
  recipe.ingredients.each do |ingredient|
    #...
  end
end

You'll also need to do has_many :ingredients, through: :recipe_ingredients on the Recipe model.

CodePudding user response:

@Eyeslandic's suggestion the use the blueprinter gem gave me exactly what I needed.

  • Related