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.