带有嵌套属性的Rails before_update回调 [英] Rails before_update callback with nested attributes

查看:89
本文介绍了带有嵌套属性的Rails before_update回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个模型(先叫AB).

A has_many b s和B belongs_to A.

class A < ApplicationRecord
  has_many :bs, dependent: :destroy, inverse_of: :a
  accepts_nested_attributes_for :bs, reject_if: :all_blank, allow_destroy: true
  validates_associated :bs
end


class B < ApplicationRecord
  belongs_to :a, inverse_of: :bs
  before_update :do_something, unless: Proc.new { |b| b.a.some_enum_value? if a }

  def do_something
    self.some_field = nil
  end

end

除此之外,B具有before_update回调,如果 A已设置some_enum_value,则将some_field设置为零.

由于此关系用于嵌套表格,因此仅当我更新属性表格B时,才会调用B中的before_update.如果我仅更改值形式A,则不会调用该回调.

在更新A时如何调用Bbefore_update?

提前谢谢.

解决方案

对于属于关联,可以使用touch选项:

class B < ApplicationRecord
  belongs_to :a, inverse_of: :bs, touch: true
end

当您更新B时,哪个会更新a.updated_at. 但是,对于has_many关系,此选项不存在,因为它可能会对性能造成潜在的灾难性影响(如果A的个数为1000或更多,则Bs.)

但是您可以自己滚动:

class A < ApplicationRecord
  has_many :bs, dependent: :destroy, inverse_of: :a
  accepts_nested_attributes_for :bs, reject_if: :all_blank, allow_destroy: true
  validates_associated :bs
  after_update :cascade_update!

  def cascade_update!
    # http://api.rubyonrails.org/classes/ActiveRecord/Batches.html#method-i-find_each
    bs.find_each(batch_size: 100) do |b|
      b.update!(updated_at: Time.zone.now)
    end
  end
end

I have two models (lets call then A and B).

A has_many bs and B belongs_to A.

class A < ApplicationRecord
  has_many :bs, dependent: :destroy, inverse_of: :a
  accepts_nested_attributes_for :bs, reject_if: :all_blank, allow_destroy: true
  validates_associated :bs
end


class B < ApplicationRecord
  belongs_to :a, inverse_of: :bs
  before_update :do_something, unless: Proc.new { |b| b.a.some_enum_value? if a }

  def do_something
    self.some_field = nil
  end

end

Other than that, B has a before_update callback that sets some_field to nil if A has some_enum_value set.

Since this relation is used on a nested form, that before_update from B is only being called if I update a attribute form B. If I only change a value form A that callback is not called.

How can I call B's before_update when A is updated?

Thanks in advance.

解决方案

For belongs to associations you can use the touch option:

class B < ApplicationRecord
  belongs_to :a, inverse_of: :bs, touch: true
end

Which would update a.updated_at when you update B. However this option does not exist for has_many relations since it could have potentially disastrous performance consequences (If an A has 1000s or more Bs).

You can roll your own however:

class A < ApplicationRecord
  has_many :bs, dependent: :destroy, inverse_of: :a
  accepts_nested_attributes_for :bs, reject_if: :all_blank, allow_destroy: true
  validates_associated :bs
  after_update :cascade_update!

  def cascade_update!
    # http://api.rubyonrails.org/classes/ActiveRecord/Batches.html#method-i-find_each
    bs.find_each(batch_size: 100) do |b|
      b.update!(updated_at: Time.zone.now)
    end
  end
end

这篇关于带有嵌套属性的Rails before_update回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆