right way to create associated records in Rails?

213 views
Skip to first unread message

Bryan

unread,
Apr 15, 2008, 12:50:33 PM4/15/08
to pdx...@googlegroups.com
I'm curious if there's a "right" way to create associated records in a
Rails app, particularly when the associated record ids are passed in
from a form. This seems like Rails 101, but I just realized that I've
done this a few different ways in the past, and none of them seem like
they're quite right. Here's what I do now (pastie:
http://pastie.caboo.se/181161):

class Recipe < ActiveRecord::Base
has_many :ingredients_recipes
has_many :ingredients, :through => :ingredients_recipes
after_save :add_ingredients
attr_writer :ingredient_ids

private

def add_ingredients
if @ingredient_ids
ingredients_recipes.destroy_all
@ingredient_ids.each do |ingredient_id|
ingredient = Ingredient.find(ingredient_id)
ingredients << ingredient if ingredient
end
end
end

end


Is this the best way to go about this, or is there a better way that
I've missed? It seems like there would be a built-in feature for this
or a plugin out there. Or at least a best practice. I do like this
approach for its ease of testing (I would much rather test models than
controllers).

Thanks,
Bryan

Bryan

unread,
Apr 15, 2008, 12:59:16 PM4/15/08
to pdx...@googlegroups.com
I should clarify that I'm referring to has_many :through associations
since they don't have the built-in ingredient_ids= method (which I
haven't tried for habtm, or other associations though, so I need to
check that out..)

Sam Livingston-Gray

unread,
Apr 15, 2008, 1:05:42 PM4/15/08
to pdx...@googlegroups.com
This isn't *the* answer, but here's a simple refactoring...

def add_ingredients
ingredients_recipes.destroy_all
ingredients << Ingredient.find(@ingredient_ids)
end

I think using << works -- in looking at the source for
ActiveRecord::Associations::AssociationCollection (in 1.2.x), the <<
method has the following code and comments:

# Add +records+ to this association. Returns +self+ so method
calls may be chained.
# Since << flattens its argument list and inserts each record,
+push+ and +concat+ behave identically.
def <<(*records)

-Sam

Bryan

unread,
Apr 15, 2008, 1:10:04 PM4/15/08
to pdx...@googlegroups.com
Ah, thanks Sam, that's a lot cleaner and seems to work fine.

Sam Livingston-Gray

unread,
Apr 15, 2008, 2:50:24 PM4/15/08
to pdx...@googlegroups.com
Also, in the recipe application I never finish writing, I settled on
"Use" as the name for the IngredientRecipe model.

class Ingredient < ActiveRecord::Base
has_many :uses
has_many :recipes, :through => :uses
end

It doesn't read quite as well from the Recipe side, but it's much
easier to type. (=

-Sam

Reply all
Reply to author
Forward
0 new messages