Nested attributes with update_attributes()


#1

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


#2

Peter B. wrote:

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


#3

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


#4

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


#5

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 :slight_smile:

Thanks for help

Peter


#6

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
:frowning:
But isn’t it something very common and natural to use associations in
object oriented design?

Peter


#7

Peter B. 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 :slight_smile:

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.


#8

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


#9

Peter B. 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 B. 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.