Assign_attributes only assigns child attr_accessor values if a table column is changed on updates

1k Views Asked by At

Here's the set up

class Order
  has_many :items
  accepts_nested_attributes_for :items
end

class Item
  belongs_to :order
  attr_accessor :discount # this is NOT a table column, just a virtual attribute I want to be able to assign
end

Let's say I have an existing order @order. I noticed that if I were to try to assign discount on the child Item from the Order model, this only works if some other attribute on the child Item is changed.

@order.assign_attributes({"items_attributes"=>[{"id"=>40, "discount"=>"test"}]})
@order.items.first.discount
=> nil

@order.assign_attributes({"items_attributes"=>[{"id"=>40, "discount"=>"test", "admin_notes"=>"hello"}]})
# where admin_notes is a table column for Item
@order.items.first.discount
=> "test
@order.items.first.admin_notes
=> "hello"

Is there a quick setting change or something to change this? I'd like attr_accessor attributes to always be assigned even if no attributes are changing?

2

There are 2 best solutions below

0
james On BEST ANSWER

FWIW if anyone stumbles upon this.... it appears to have resolved itself. Was working on something else today and noticed that I can't duplicate the original error anymore. A lot has changed in code, not going to go back and dig into this to figure out what happened. I do know that key configuration variables that could have affected this have not changed, e.g., inverse_of flag set on both models, Order model set to accepts_nested_attributes_for :items

9
Patrick C On

I see that your original question asked to be able to use attr_accessor, however it doesn't seem possible. I was able to get this working only by removing attr_accessor.

It appears that attr_accessor actually has the opposite behavior of making an attribute inaccessible instead of allowing it when using accepts_nested_attributes_for. Perhaps a bug? The docs do not provide any examples. As long as you are doing params.permit on the Controller-side, this is still safe to use without attr_accessor.

class Order
  has_many :items
  accepts_nested_attributes_for :items
end

class Item
  belongs_to :order
end

Calling it from Rails console:

@order = Order.find(1)
@order.assign_attributes({"items_attributes"=>[{"id" => 1, "discount_cents" => "234"}]})
@order.save

#  Order Load (0.4ms)  SELECT "orders".* FROM "orders" WHERE "orders"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
#  Item Load (0.3ms)  SELECT "items".* FROM "items" WHERE "items"."order_id" = $1 AND "items"."id" = $2  [["order_id", 1], ["id", 1]]
#   (0.1ms)  BEGIN
#  Item Update (0.3ms)  UPDATE "items" SET "discount_cents" = $1 WHERE "items"."id" = $2  [["discount_cents", 234], ["id", 1]]
# => true
#   (5.8ms)  COMMIT

@order.assign_attributes({"items_attributes"=>[{"id" => 1, "discount_cents" => "789"}]})
# => nil
#  Item Load (0.4ms)  SELECT "items".* FROM "items" WHERE "items"."order_id" = $1 AND "items"."id" = $2  [["order_id", 1], ["id", 1]]
@order.items.first.attributes
# => {"order_id"=>1, "id"=>1, "discount_cents"=>789}

Hope that provides a solution to your question :)