Updating parent-child objects in a single transaction

Is there any way to update a parent and child object in a single
transaction without having to explicitly demarcate that transaction?
I’ve got the following two objects:

class CostItem < ActiveRecord::Base
has_one :cost :dependent => :destroy
validates_associated :cost
end

class Cost < ActiveRecord::Base
belongs_to :cost_item
validates_numericality_of :amount
end

In my controller, I’ve got the following:

def create
@cost_item = CostItem.new(params[:cost_item])
@cost_item.build_cost(params[:cost])
if @cost_item.save

else
# Handle the error
end
end

def update
@cost_item = Item.find(params[:id])
if @cost_item.cost.update_attributes(params[:cost]) &&
@cost_item.update_attributes(params[:cost_item])

else
# Handle the error
end
end

The ‘create’ method works transactionally - if either @cost_item or
@cost is invalid, nothing gets saved. However, ‘update’ is not
transactional: if either @cost_item or @cost is invalid, then the
valid one still gets saved.

The only way I can see around this is to put a transaction around it
and use methods that throw an exception to trigger the rollback:

def update
@cost_item = Item.find(params[:id])
begin
CostItem.transaction do
@cost_item.cost.update_attributes!(params[:cost])
@cost_item.update_attributes!(params[:cost_item])

end
rescue ActiveRecord::RecordInvalid
# Handle the error
end
end

However this is a bit more verbose than I hoped. Is there any way to
make the updates of related objects fully transactional - in the same
manner as creation - without having to explicitly demarcate the
transaction?

Thanks,

Ben

I am trying to do something very similar and i’ve got update to work,
however rails is failing on my create hierarchical xml. to use you
example, I am sending something like:

<cost_item>
blah
blah

10

</cost_item>

what xml format do you post to get this to work?

Ben T. wrote:

belongs_to :cost_item
else
# Handle the error
end
end

def update
@cost_item = Item.find(params[:id], :include => :cost)
@cost_item.cost.attributes = params[:cost]
if @cost_item.update_attributes(params[:cost_item])
@cost_item.cost.save!

else
# Handle the error
end
end

If they’re always updated together you can move the save
of cost to the model:

class CostItem
after_update ‘cost.save!’
end

You should still wrap the two saves in a transaction in
case there are DB problems, but it’s best to avoid using
transactions to recover from validation failures.


We develop, watch us RoR, in numbers too big to ignore.

On 17 Mar 2008, at 15:58, Mark Reginald J. wrote:

transactions to recover from validation failures.
before_save, the actual save and after_save are wrapped in a
transaction for you.

Fred

I’m just using a HTTP POST with parameters, not XML. I’m not sure how
it’d
work with XML.

Cheers,

Ben

On Mon, May 5, 2008 at 3:39 PM, coconut [email protected] wrote:

> has_one :cost :dependent => :destroy > def create > @cost_item = Item.find(params[:id]) > transactional: if either @cost_item or @cost is invalid, then the > @cost_item.update_attributes!(params[:cost_item]) thetransaction? > > Thanks, > > Ben


iZepto: Timesheets Made Human
http://izepto.com

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs