Rails 嵌套属性和不断变化的关联

Rails nested attributes and changing associations

这个让我难住了一整天!

我有以下型号:

暴涨class

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

泵模式

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

控制class

class Control < ApplicationRecord
  belongs_to :equipment, polymorphic: true

控制模式

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

我正在尝试更新 ControlPump 之间的关联。以下作品:

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

但是下面的不是,我不明白为什么。

[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!'

上下文是我有一个 Pump 表单,在编辑我的 Pump 时,select 下拉列表中有一个控件列表。我只想选择与泵关联的控件。

更新1:回答下面的问题

 [468] pry(main)> Pump.first.update(control_attributes: {id: 62})
  Pump Load (1.0ms)  SELECT  "pumps".* FROM "pumps" ORDER BY "pumps"."id" ASC LIMIT   [["LIMIT", 1]]
   (0.3ms)  BEGIN
  Control Load (0.4ms)  SELECT  "controls".* FROM "controls" WHERE "controls"."equipment_id" =  AND "controls"."equipment_type" =  LIMIT   [["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!'

当您对链接模型使用 accepts_nested_attributes_for 时,如果提供的属性没有 id 参数,它将创建新记录。当属性提供 id 参数时,它将更新与父记录链接的现有记录。

ActiveRecord::RecordNotFound: Couldn't find Control with ID=62 for Pump with ID=1:此错误表明没有找到具有上述 ID 的 Pump 对象的 Control 记录。

您可以为泵添加新的控制记录:

Pump.first.update(control_attributes: { attribute1: 'attribute1_value' } )

这将创建与 ID 为 1Pump 对象关联的新 Control 记录。现在您可以按如下方式再次更新:

Pump.first.update(control_attributes: { id: 1, attribute1: 'updated_attribute1_value' } )

注意新创建的Control记录的id取为1.

请阅读 through the documentation 了解更多详情。

希望对您有所帮助!

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

Rails的嵌套属性不是这样用的!上面的代码表示:

找一个id为62的控件,它的equipment_type应该是"Pump",它的equipment_id应该是Pump.first.id,然后更新为extra 参数,你没有提供。

您出现此错误是因为在第一步中,id 为 62 的控件 equipment_id 不是 Pump.first.id

例如,要更新 ID 为 60 的控件的名称,属于 Pump.first,正确关联:

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

您可以覆盖模型中的嵌套属性 setter 方法,这样它也可以直接更新外键列。

# pump.rb
def control_attributes=(attributes)
  if (new_control = Control.find_by(id: attributes[:id]))
    self.control_id = new_control.id
  end

  super
end

注意:直接分配关系时要小心(即 self.control = new_control),因为如果它是用 :dependent 选项定义的 has_one 关联,这可能会导致一些意想不到的副作用这会导致删除记录。