Rails: has_many with extra details?
While I'm not a complete Ruby/Rails newb, I'm still pretty green and I'm trying to figure out how to structure some model relationships. The simplest example I can think of is the idea of "recipes" for cooking.
A recipe consists of one or more ingredients and the associated quantity of each ingredient. Assume we have a master list in the database of all ingredients. That suggests two simple models:
class Ingredient < ActiveRecord::Base
# ingredie开发者_StackOverflownt name,
end
class Recipe < ActiveRecord::Base
# recipe name, etc.
end
If we just wanted to associate Recipes with Ingredients, that's as simpling as adding the appropriate belongs_to
and has_many
.
But what if we want to associate additional information with that relationship? Each Recipe
has one or more Ingredients
, but we want to indicate the quantity of the Ingredient
.
What is the Rails way to model that? Is it something along the lines of a has_many through
?
class Ingredient < ActiveRecord::Base
# ingredient name
belongs_to :recipe_ingredient
end
class RecipeIngredient < ActiveRecord::Base
has_one :ingredient
has_one :recipe
# quantity
end
class Recipe < ActiveRecord::Base
has_many :recipe_ingredients
has_many :ingredients, :through => :recipe_ingredients
end
Recipes and Ingredients have a has and belongs to many relationship, but you want to store additional information for link.
Essentially what you are looking for is a rich join model. But, a has_and_belongs_to_many relationship is not flexible enough to store the additional information you require. Instead you will need to use a has_many :through relatinship.
This is how I would set it up.
recipes columns: instructions
class Recipe < ActiveRecord::Base
has_many :recipe_ingredients
has_many :ingredients, :through => :recipe_ingredients
end
recipe_ingredients columns: recipe_id, ingredient_id, quantity
class RecipeIngredients < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
end
ingredient columns: name
class Ingredient < ActiveRecord::Base
has_many :recipe_ingredients
has_many :recipes, :through => :recipe_ingredients
end
This will provide a basic representation of what you're looking to do. You may want to add a validation to RecipeIngredients to ensure that each ingredient is listed once per recipe, and a callback to fold duplicates into one entry.
http://railsbrain.com/api/rails-2.3.2/doc/index.html?a=M001888&name=has_and_belongs_to_many
http://railsbrain.com/api/rails-2.3.2/doc/index.html?a=M001885&name=has_many
How about:
- class Ingredient (belongs to recipe, has many ingredientrecipecounts)
- class Recipe (has many ingredients, has many ingredientrecipecounts)
- class IngredientRecipeCount (belongs to ingredient, belongs to recipe)
This is not so much the Rails way as just establishing one more relation between the data in the database. It's not really a "has and belongs to many" because each ingredient only has one count per recipe, and each recipe one count per ingredient.. Which is the same count.
精彩评论