So, imagine the following:
class User < ActiveRecord::Base
has_many :emails
has_one :address
end
class Email < ActiveRecord::Base
belongs_to :user
end
class Address < ActiveRecord::Base
belongs_to :user
end
Now I am wondering what the correct Ruby-way to create a user object is
in the controller when through a form, a new user, a couple of email
addresses and a street address is posted.
The obvious @user = User.new(params[:user]) was my first attempt. The
problem here is that @user.emails.build and @user.address.build calls
are fine, until we try to save the @user object. It then gives an error
that it can not save the emails and address, because it doesn’t know
what user_id to write in its foreign keys.
The only way I could devise was an intricate scheme where I would first
try to save the emails and address, and then the user object. Whenever
something would fail along the way it would rollback any previous
changes and destroy the correct object. This results in a very complex
non-ruby if/then stack which I really hate.
How am I supposed to save an object spanning multiple models at once and
consistent?
There is an explanation of this in the Rails API documentation here:
Scroll down to the section: Unsaved objects and associations
On Jul 7, 9:29 am, Chris D. [email protected]
After looking through that documentation and your scenario, I suppose
one possibility is to perform your saves in a database transaction:
transaction do
save the association objects
save the user object
end
So if any failure occurs during save the whole transaction gets rolled
back. I’m not sure if this is the best way to do this though.
It does seem like it would be nice if Rails would automatically create
a transaction if an object has unsaved associations. Then perform
saves on the associations then save the parent object. But, I’m sure
there’s probably a good reason that it doesn’t.
That being said, I’m more used to a system that maintains an in-memory
object graph that takes care of all those details for you. However,
that opens up a whole new level of complexity, and a new set of
potential gotchas to have to deal with.
So, imagine the following:
class Recipe
has_many :ingredients
end
class Ingredient
belongs_to :recipe
end
this is my console output
r = Recipe.new(:name => ‘spaghetti carbonara’)
=> #<Recipe id: nil, name: “spaghetti carbonara”, description: nil,
created_at: nil, updated_at: nil>
r.ingredients.build( :name => ‘pasta’ )
=> #<Ingredient id: nil, name: “pasta”, recipe_id: nil, created_at:
nil, updated_at: nil>
r.ingredients
=> [#<Ingredient id: nil, name: “pasta”, recipe_id: nil, created_at:
nil, updated_at: nil>]
r.ingredients.first.recipe_id
=> nil
r.save
=> true
r.ingredients.first.recipe_id
=> 4
Im on rails 2.1 out of the box.
On Jul 7, 2:29 pm, Chris D. [email protected]
Well then…
Disregard my last post. Apparently, at least with Rails 2.1, it does
seems to take care of the associations when saving the parent.
Rails has always done wrapped new parent/child commits in a
transaction; that’s nothing new to 2.1.
I suspect that Chris’ issue has to do with validations. My guess is
that what he’s omitted from his class declarations in the initial post
is that he’s validating for the presence/numericality of user_id but
it’s not yet known. If my guess is correct then it’s a pretty simple
refinement of the validation call.
Well then…
Disregard my last post. Apparently, at least with Rails 2.1, it does
seems to take care of the associations when saving the parent.