Hi, I am trying to use the update_attributes on object with nested attributes and I am getting the following error: ProductPrice expected, got HashWithIndifferentAccess Here''s the situation (simplified): class Product < ActiveRecord::Base belongs_to :productPrice end class ProductPrice < ActiveRecord::Base has_one :product, :dependent => true validates_presence_of :costprice, :listprice end my parameters look something like this: params = { :product => { :productno => "something", :description => "something else", :productPrice => { :costprice => 30.34, :listprice => 4.45 } } } I get the error when I run product.update_attributes(params[:product]) Can anybody give me a hint on how to work with nested attributes? Thanks Peter
Peter Bohm wrote:> I am trying to use the update_attributes on object with nested > attributes and I am getting the following error: > > ProductPrice expected, got HashWithIndifferentAccess > > Here''s the situation (simplified): > > class Product < ActiveRecord::Base > belongs_to :productPrice > end > > class ProductPrice < ActiveRecord::Base > has_one :product, > :dependent => true > > validates_presence_of :costprice, :listprice > > end > > my parameters look something like this: > params = { > :product => { > :productno => "something", > :description => "something else", > :productPrice => { > :costprice => 30.34, > :listprice => 4.45 > } > } > }This''d probably work: params[:product][:productPrice] = ProductPrice.new( params[:product][:productPrice] ) product.update_attributes( params[:product] ) or product.build_product_price( params[:product][:productPrice] ) params[:product].delete( :productPrice ) product.update_attributes( params[:product] ) Though: 1. You should use underscore format for your belongs_to (belongs_to :product_price) 2. If you nest the names in your form the form helpers aren''t going to auto fill form values unless you use the form helper compound objects patch. So it''d probably be nicer not to nest the parameters and do: product.build_product_price( params[:product_price] ) product.update_attributes( params[:product] ) -- We develop, watch us RoR, in numbers too big to ignore.
I assume that you have the correct database schema to match the attributes? Also, but I don''t know if there is any difference, try: product.attributes params[:product] If the latter fails, can you reply with your DB schema/sql statements? Cheers Nic
Hi Mark, thank you very much for answer the camel case was just the typo - anyway, thanks for pointing out As for the rest... how I''ve been doing it is very similiar to what you suggested: product.product_price.update_attributes(params[:product_price]) product.update_attributes(params[:product]) I don''t use build_product_price because I am not able to use it with single table inheritance - e.g. ProductPrice is a "base class" and it''s extended by NetPrice, MarkupPrice, MarkdownPrice, etc. How do I pass the actual implementation (class type) to the build_xxx method? Anyways, why I brought the whole thing up is because it''s kind of awkward to have another instance variable for nesting. There is a hint of this in the agile book (table on p. 343) where the last row uses user[address][city]=Wien for form parameters. That is translated to {:user => {:address => {:city ="Wien"}}} and then used as user.update_attributes(params[:user]). However, I was neither able to get the form helpers to produce the nested field nor use the update_attributes :-) Thanks for help Peter Mark Reginald James wrote:> Peter Bohm wrote: > >> I am trying to use the update_attributes on object with nested >> attributes and I am getting the following error: >> >> ProductPrice expected, got HashWithIndifferentAccess >> >> Here''s the situation (simplified): >> >> class Product < ActiveRecord::Base >> belongs_to :productPrice >> end >> >> class ProductPrice < ActiveRecord::Base >> has_one :product, >> :dependent => true >> validates_presence_of :costprice, :listprice >> end >> >> my parameters look something like this: >> params = { >> :product => { >> :productno => "something", >> :description => "something else", >> :productPrice => { >> :costprice => 30.34, >> :listprice => 4.45 >> } >> } >> } > > > This''d probably work: > > params[:product][:productPrice] = ProductPrice.new( > params[:product][:productPrice] ) > product.update_attributes( params[:product] ) > > or > > product.build_product_price( params[:product][:productPrice] ) > params[:product].delete( :productPrice ) > product.update_attributes( params[:product] ) > > Though: > > 1. You should use underscore format for your belongs_to (belongs_to > :product_price) > 2. If you nest the names in your form the form helpers aren''t going to > auto > fill form values unless you use the form helper compound objects > patch. > > So it''d probably be nicer not to nest the parameters and do: > > product.build_product_price( params[:product_price] ) > product.update_attributes( params[:product] ) >
Hi Nic, thanks for reply product.attributes produces the same error message please see my other post for more explanation Do you have something similiar working? I would be very grateful for any hint how to make it work without workarounds Here''s my schema...it''s simplified because in actual situation it uses inheritance on products, and product prices create table products ( id int4 DEFAULT nextval(''"products_id_seq"''::text) not null, productNo varchar(250) not null, description varchar(250) not null, product_price_id int4 not null, remark text, brand_id int4, model_id int4, contact_id int4, created_at timestamp not null, updated_at timestamp not null, primary key (id) ); create table product_prices ( id int4 DEFAULT nextval(''"product_prices_id_seq"''::text) not null, costPrice numeric not null, listPrice numeric not null, primary key (id) ); alter table products add constraint fk_product_product_price foreign key (price_id) references product_prices; Thank you very much Peter Nic Williams wrote:>I assume that you have the correct database schema to match the attributes? > >Also, but I don''t know if there is any difference, try: product.attributes >params[:product] > >If the latter fails, can you reply with your DB schema/sql statements? > >Cheers >Nic > >
Peter Bohm wrote:> product.product_price.update_attributes(params[:product_price]) > product.update_attributes(params[:product])I didn''t know there was an update_attributes method for belongs_to associations. Does this code work?> I don''t use build_product_price because I am not able to use it with > single table inheritance - e.g. ProductPrice is a "base class" and it''s > extended by NetPrice, MarkupPrice, MarkdownPrice, etc. How do I pass the > actual implementation (class type) to the build_xxx method?You should be able to use build_net_price, build_markup_price, and build_markdown_price.> Anyways, why I brought the whole thing up is because it''s kind of > awkward to have another instance variable for nesting. There is a hint > of this in the agile book (table on p. 343) where the last row uses > user[address][city]=Wien for form parameters. That is translated to > {:user => {:address => {:city ="Wien"}}} and then used as > user.update_attributes(params[:user]). > However, I was neither able to get the form helpers to produce the > nested field nor use the update_attributes :-)Yes, p.343 does imply that update_attributes works for nested parameters. Though it''d probably work if address was an aggregration, it won''t work for associations. Perhaps you should submit an erratum at http://books.pragprog.com/titles/rails/errata . Nested parameters also don''t work with form helpers unless you use a patch I''ve placed on the dev site. Even then, the update_attributes and new methods won''t work unless you fiddle with the params to properly build the object chain. -- We develop, watch us RoR, in numbers too big to ignore.
Hi Mark, the code really works - I guess the first statement will create and save product_price object (which doesn''t have any database reference on Product) and the second will just create product and associate it with ProductPrice... build_net_price, etc. doesn''t work - I get NoMethodError Thank you very much for the rest of the explanation on the nested associations...I spent a few days on it not being able to work it out :-( But isn''t it something very common and natural to use associations in object oriented design? Peter Mark Reginald James wrote:> Peter Bohm wrote: > >> product.product_price.update_attributes(params[:product_price]) >> product.update_attributes(params[:product]) > > > I didn''t know there was an update_attributes method for belongs_to > associations. Does this code work? > >> I don''t use build_product_price because I am not able to use it with >> single table inheritance - e.g. ProductPrice is a "base class" and it''s >> extended by NetPrice, MarkupPrice, MarkdownPrice, etc. How do I pass the >> actual implementation (class type) to the build_xxx method? > > > You should be able to use build_net_price, build_markup_price, and > build_markdown_price. > >> Anyways, why I brought the whole thing up is because it''s kind of >> awkward to have another instance variable for nesting. There is a hint >> of this in the agile book (table on p. 343) where the last row uses >> user[address][city]=Wien for form parameters. That is translated to >> {:user => {:address => {:city ="Wien"}}} and then used as >> user.update_attributes(params[:user]). >> However, I was neither able to get the form helpers to produce the >> nested field nor use the update_attributes :-) > > > Yes, p.343 does imply that update_attributes works for nested > parameters. Though it''d probably work if address was an > aggregration, it won''t work for associations. Perhaps you should > submit an erratum at http://books.pragprog.com/titles/rails/errata . > > Nested parameters also don''t work with form helpers unless you > use a patch I''ve placed on the dev site. Even then, the > update_attributes and new methods won''t work unless you fiddle > with the params to properly build the object chain. >
Peter Bohm wrote:>>>product.product_price.update_attributes(params[:product_price]) >>>product.update_attributes(params[:product]) >> >>I didn''t know there was an update_attributes method for belongs_to >>associations. Does this code work?> the code really works - I guess the first statement will create and save > product_price object (which doesn''t have any database reference on > Product) and the second will just create product and associate it with > ProductPrice... Yes, I see now that product_price is just an AR object for which update_attributes works in its normal way. Associations just have some extra or overridden methods besides the normal ones. Useful to keep in mind. Peter Bohm wrote: > build_net_price, etc. doesn''t work - I get NoMethodError OK, I thought you had definitions "belongs_to :net_price", etc. -- We develop, watch us RoR, in numbers too big to ignore.
Hi Mark, Of course the point is to have a reference to ProductPrice and choose the implementation at runtime Thanks for the link to your patch as well Peter Mark Reginald James wrote:> Peter Bohm wrote: > >>>> product.product_price.update_attributes(params[:product_price]) >>>> product.update_attributes(params[:product]) >>> >>> >>> I didn''t know there was an update_attributes method for belongs_to >>> associations. Does this code work? >> > > > the code really works - I guess the first statement will create and > save > > product_price object (which doesn''t have any database reference on > > Product) and the second will just create product and associate it with > > ProductPrice... > > Yes, I see now that product_price is just an AR object for which > update_attributes works in its normal way. Associations just have > some extra or overridden methods besides the normal ones. Useful to > keep in mind. > > > Peter Bohm wrote: > > build_net_price, etc. doesn''t work - I get NoMethodError > > OK, I thought you had definitions "belongs_to :net_price", etc. >