0
votes

Is there any (clean) way to access a single association when using a has_many through association?

For example, to get all of a Recipe's Ingredients, I can write:

@ingredients = @recipe.ingredients

However, if I have an ingredient already selected, and I want to add data to the join table for the recipe/ingredient, I can't figure out the clean way to add the data.

I cannot access the single ingredient association's amount using

ingredient = Ingredient.find(an_id)  

amount = @recipe.ingredient.amount

What is the method of select a single ingredients information that is stored in the join table?

4
So you have found the Recipe you want, you have found the Ingredients for that Recipe. Do you want to edit the attributes of one of these Ingredients or do you want to add a new Ingredient for the previouslt selected Recipe? Or is there another Join table on Ingredients that you want to populate?OpenCoderX
There is a join table on recipes and ingredients that holds information about ingredients specific to the recipe they are related to, i.e. amounts for each ingredient.Christian Benincasa

4 Answers

1
votes

Options :select is very useful in this case. For example

class Recipe
  has_many :ingredients, through: ingredients_recipes, select: "ingredinets.*, ingredients_recipes.some_additional_column as some_additional_column"
  ....

Then just call

Recipe.first.ingredients.first.some_additional_column # => profit!

You can use it in your scopes too.

0
votes

You could @ingredient = @recipe.ingredients.first (if it is truly the only one, not sure how you could know this for sure.)

Assuming you already have the ingredient selected, as in a variable, you can set them like any other ActiveRecord object.

@ingredient = @recipe.ingredients.first
@ingredient.amount = 15 #or @ingredient.amount = params[:amount] (if you get the new value from POST)
@ingredient.unit = "Ounces"
@ingredient.save

If you just want to retrieve the attributes of each ingredient for a recipe and do something with them, like display them in some html tags in a view you could try this.

@ingredients = @recipe.ingredients
   @ingredients.each do |ingr|
    info = %w[amount, brand, id, recipe_id , unit].map do |attr|
      ingr.send(attr)
    end

at the end of each inner loop the array 'info' will be all the attributes of one ingredient.(assuming you include them in info array)

This is an example of a table you might use to store the associations, it should work with the rails :through => :recipe_ingedient method:

CREATE TABLE `recipe_ingredient` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `recipe_id` int(11) NOT NULL,
  `ingedient_id` int(11) NOT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci$$
0
votes

The join table is important for you, so it should have a name. You can call it recipe_ingredient, but a real name helps not to confuse the things mentally. (and it help us discussing about the problem)

Given the join table is called for example slot.

class Recipe
  has_many :slots
  has_many :ingredients, :through => :slots
  ..
class Slot
  belongs_to :recipe
  belongs_to :ingredient
  ...

You get a defined slot by calling

@slot = @recipe.slots.where(:ingredients_id => @ingredient).first 
@slot.amount = 20

For extra efficiency and to ensure that each combination exists only one time, you can define an unique compound index for slot to span across both recipe_id and ingredient_id

-1
votes

I don't see how such a thing could exist. If you have many associated objects, you need to specify which one you want. That's not so much a limitation of Rails as a limitation of... I don't know, set theory? Logic? You can get the one of the associated objects with .first (ingredient = @recipe.ingredients.first), but unless you also specify an order, there's no way to be certain which one it will be.