Rails can't save a nested resource via ActiveResource
activeresource, ruby-on-rails
Solution
ActiveResource doesn't have association support out of the box. interesting idea to hijack the nested_* stuff for this. This seems to work for me:
class Asset < ActiveRecord::Base
has_many :interfaces
accepts_nested_attributes_for :interfaces
alias interfaces_attributes interfaces
def to_xml(options = {})
super(options.merge({:methods => :interfaces_attributes}))
end
end
move the to_xml options into the controller if you like. might want to also make sure Interface has the right attr_protected's.
Problem
I'm having a problem using ActiveResource to save a model with a nested resource: I can load and save a non-nested resource with no problems, and I can load a resource plus its nested resources fine, but saving the resource fails. This problem occurs for me with REE 1.8.7/Rails 2.3.9 and Ruby 1.9.2-p0/Rails 3.0.2. The examples below are from Rails 3/AR 3. There are two models in my sample app: - Asset - Interface Assets have many Interfaces, Interfaces belong to Assets. routes.rb ``` Rails3Testapp::Application.routes.draw do resources :interfaces resources :assets end ``` Relevant AssetController actions ``` def index @assets = Asset.all respond_to do |format| format.html # index.html.erb format.xml { render :xml => @assets, :include => [ :interfaces ] } end end def show @asset = Asset.find(params[:id]) respond_to do |format| format.html # show.html.erb format.xml { render :xml => @asset, :include => [ :interfaces ] } end end def update @asset = Asset.find(params[:id]) respond_to do |format| if @asset.update_attributes(params[:asset]) format.html { redirect_to(@asset, :notice => 'Asset was successfully updated.') } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @asset.errors, :status => :unprocessable_entity } end end end ``` Models ``` class Asset < ActiveRecord::Base has_many :interfaces accepts_nested_attributes_for :interfaces end class Interface < ActiveRecord::Base belongs_to :asset end ``` Steps to Replicate ``` >> require 'active_resource' => true >> class Asset < ActiveResource::Base >> self.site = 'http://localhost:3000/' >> end >> test_asset = Asset.first => #<Asset:0x00000100a86690 @attributes={"created_at"=>2010-12-07 16:24:59 UTC, "description"=>"Testing testing", "id"=>1, "name"=>"Test Asset #1", "updated_at"=>2010-12-07 16:24:59 UTC, "interfaces"=>[#<Asset::Interface:0x00000100a7dc98 @attributes={"asset_id"=>1, "created_at"=>2010-12-07 18:01:15 UTC, "id"=>1, "name"=>"eth0", "updated_at"=>2010-12-07 18:01:15 UTC}, @prefix_options={}>, #<Asset::Interface:0x00000100a7a8e0 @attributes={"asset_id"=>1, "created_at"=>2010-12-07 18:04:49 UTC, "id"=>2, "name"=>"eth1", "updated_at"=>2010-12-07 18:04:49 UTC}, @prefix_options={}>]}, @prefix_options={}> >> test_asset.save ActiveResource::ServerError: Failed. Response code = 500. Response message = Internal Server Error . from /Users/threetee/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activeresource-3.0.3/lib/active_resource/connection.rb:147:in `handle_response' from /Users/threetee/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activeresource-3.0.3/lib/active_resource/connection.rb:114:in `request' from /Users/threetee/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activeresource-3.0.3/lib/active_resource/connection.rb:91:in `block in put' from /Users/threetee/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activeresource-3.0.3/lib/active_resource/connection.rb:217:in `with_auth' from /Users/threetee/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activeresource-3.0.3/lib/active_resource/connection.rb:91:in `put' from /Users/threetee/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activeresource-3.0.3/lib/active_resource/base.rb:1307:in `update' from /Users/threetee/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activeresource-3.0.3/lib/active_resource/observing.rb:11:in `update_with_notifications' from /Users/threetee/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activeresource-3.0.3/lib/active_resource/base.rb:1117:in `save' from /Users/threetee/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activeresource-3.0.3/lib/active_resource/validations.rb:87:in `save_with_validation' from /Users/threetee/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activeresource-3.0.3/lib/active_resource/observing.rb:11:in `save_with_notifications' from (irb):33 from /Users/threetee/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>' ``` Log ``` Started PUT "/assets/1.xml" for 127.0.0.1 at 2010-12-07 11:58:40 -0800 Processing by AssetsController#update as XML Parameters: {"asset"=>{"created_at"=>2010-12-07 16:24:59 UTC, "description"=>"Testing testing", "id"=>1, "name"=>"Test Asset #1", "updated_at"=>2010-12-07 16:24:59 UTC, "interfaces"=>[{"asset_id"=>1, "created_at"=>2010-12-07 18:01:15 UTC, "id"=>1, "name"=>"eth0", "updated_at"=>2010-12-07 18:01:15 UTC}, {"asset_id"=>1, "created_at"=>2010-12-07 18:04:49 UTC, "id"=>2, "name"=>"eth1", "updated_at"=>2010-12-07 18:04:49 UTC}]}, "id"=>"1"} SQL (0.8ms) SELECT name FROM sqlite_master WHERE type = 'table' AND NOT name = 'sqlite_sequence' Asset Load (0.2ms) SELECT "assets".* FROM "assets" WHERE ("assets"."id" = 1) LIMIT 1 WARNING: Can't mass-assign protected attributes: id Completed in 161ms ActiveRecord::AssociationTypeMismatch (Interface(#2154409400) expected, got ActiveSupport::HashWithIndifferentAccess(#2154546040)): app/controllers/assets_controller.rb:62:in `block in update' app/controllers/assets_controller.rb:61:in `update' ``` Anybody have any ideas on this one? I'm stumped. Thanks in advance for any help!