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?
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
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.
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: