ruby-on-railsnested-attributesaccepts-nested-attributes

Rails nested attributes and changing associations


This one has had me stumped all day!

I have the following models:

Pump class

class Pump < ApplicationRecord
  has_one :control, as: :equipment
  accepts_nested_attributes_for :control

Pump Schema

class CreatePumps < ActiveRecord::Migration[5.1]
  def change
    create_table :pumps do |t|
      t.references :property, foreign_key: true, null: false
      t.string :name, default: 'Pump', null: false

      t.timestamps
    end
  end
end

Control class

class Control < ApplicationRecord
  belongs_to :equipment, polymorphic: true

Control Schema

class CreateControls < ActiveRecord::Migration[5.1]
  def change
    create_table :controls do |t|
      t.belongs_to :device, foreign_key: true, index: true
      t.integer :position, index: true
      t.references :equipment, polymorphic: true, index: true
      t.belongs_to :control_type, foreign_key: true, index: true

      t.timestamps
    end
  end
end

I'm trying to update the association between a Control and a Pump. The following works:

[439] pry(main)> Pump.first.update!(control: Control.find(62))
.
.
.
=> true

But the following doesn't and I can't figure out why.

[438] pry(main)> Pump.first.update(control_attributes: {id: 62}) 
   (0.4ms)  BEGIN
   (0.4ms)  ROLLBACK
ActiveRecord::RecordNotFound: Couldn't find Control with ID=62 for Pump 
with ID=1
from /usr/local/bundle/gems/activerecord-
5.1.5/lib/active_record/nested_attributes.rb:584:in 
`raise_nested_attributes_record_not_found!'

The context is that I have a form for a Pump and when editing my Pump, there's a list of Controls in a select drop-down. I would just like to choose which Control is associated with the pump.

Update1: Answering a question from below

 [468] pry(main)> Pump.first.update(control_attributes: {id: 62})
  Pump Load (1.0ms)  SELECT  "pumps".* FROM "pumps" ORDER BY "pumps"."id" ASC LIMIT $1  [["LIMIT", 1]]
   (0.3ms)  BEGIN
  Control Load (0.4ms)  SELECT  "controls".* FROM "controls" WHERE "controls"."equipment_id" = $1 AND "controls"."equipment_type" = $2 LIMIT $3  [["equipment_id", 1], ["equipment_type", "Pump"], ["LIMIT", 1]]
   (0.3ms)  ROLLBACK
ActiveRecord::RecordNotFound: Couldn't find Control with ID=62 for Pump with ID=1
from /usr/local/bundle/gems/activerecord-5.1.5/lib/active_record/nested_attributes.rb:584:in `raise_nested_attributes_record_not_found!'

Solution

  • Pump.first.update(control_attributes: {id: 62}) 
    

    Rails's nested attributes does't work this way! The code above means:

    Find a control which's id is 62, and it's equipment_type should be "Pump", and it's equipment_id should be Pump.first.id, then update with extra params, which you did not provided.

    You got this error because in the first step, the control with id 62, it's equipment_id isn't Pump.first.id

    Like, to update name of the control which's id is 60, belongs to Pump.first, in correct association:

    Pump.first.update(control_attributes: {id: 60, name: "xxxx"})