Rails4:如何在使用accepts_nested_attributes_for时触发attr_accessor的回调

Zer*_*nel 3 callback nested-attributes attr-accessor ruby-on-rails-4

请检查伪代码:

class Team
  has_many :users
  accepts_nested_attributes_for :users, allow_destroy: true
end

class User
  belongs_to :team
  has_many :addresses
  accepts_nested_attributes_for :addresses
  attr_accessor :dummy

  before_validation :generate_addresses_attributes
  def generate_addresses_attributes
    # Use the dummy value to set the addresses_attributes
  end
end
Run Code Online (Sandbox Code Playgroud)

现在执行时team.update(users_attributes: [{"0" => { dummy: "changed!" }}])(其他字段除了dummy属性不会改变),它不会触发#generate_addresses_attributes回调,因为它认为没有任何改变,没有保存,没有回调......

所以我的问题是如何触发虚拟属性的回调,或者强制save为accept_nested_attributes_for.

谢谢!

Zer*_*nel 6

最后,我找到了两个解决方案:

  1. 在Team模型中添加回调以手动触发回调功能

  2. 使用attribute_will_change!覆盖setter方法:

    class User
      belongs_to :team
      has_many :addresses
      accepts_nested_attributes_for :addresses
      attr_accessor :dummy
    
      def dummy=(value)
        attribute_will_change!("dummy") if @dummy != value
        @dummy = value
      end
    
      ...
    end
    
    Run Code Online (Sandbox Code Playgroud)


And*_*oss 5

我发现 Rails 5.1+attributeattr_accessor这个用例效果更好。

attribute 弄脏对象,从而在保存时触发回调。

class User
  belongs_to :team
  has_many :addresses
  accepts_nested_attributes_for :addresses
  attribute :dummy, :string

  before_validation :generate_addresses_attributes
  def generate_addresses_attributes
    # Use the dummy value to set the addresses_attributes
  end
end
Run Code Online (Sandbox Code Playgroud)