当使用双重嵌套关联时,accepts_nested_attributes_for的reject_if::all_blank如何工作?

Jos*_*ady 5 ruby-on-rails nested-forms

我的模型设置如下。一切正常,除了空白零件记录允许,即使所有零件和章字段均为空白。

class Book < ActiveRecord::Base
  has_many :parts, inverse_of: :book
  accepts_nested_attributes_for :parts, reject_if: :all_blank
end

class Part < ActiveRecord::Base
  belongs_to :book, inverse_of: :parts
  has_many :chapters, inverse_of: :part
  accepts_nested_attributes_for :chapters, reject_if: :all_blank
end

class Chapter < ActiveRecord::Base
  belongs_to :part, inverse_of: :chapters
end
Run Code Online (Sandbox Code Playgroud)

拼写代码,:all_blank替换为proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }。因此,我用它代替:all_blank并添加了一些调试功能。看起来正在发生的事情是该部件的pages属性响应的原因blank?false因为它是一个实例化的哈希对象,即使它所包含的只是另一个仅包含空白值的哈希:

chapters_attributes: !ruby/hash:ActionController::Parameters
  '0': !ruby/hash:ActionController::Parameters
    title: ''
    text: ''
Run Code Online (Sandbox Code Playgroud)

只是不是要这样工作吗?

我找到了一种解决方法:

accepts_nested_attributes_for :parts, reject_if: proc { |attributes|
  attributes.all? do |key, value|
    key == '_destroy' || value.blank? ||
        (value.is_a?(Hash) && value.all? { |key2, value2| value2.all? { |key3, value3| key3 == '_destroy' || value3.blank? } })
  end
}
Run Code Online (Sandbox Code Playgroud)

但是我希望我错过了更好的方法来解决这个问题。


更新1:我尝试重新定义blank?Hash但这会导致问题。

class Hash
  def blank?
    :empty? || all? { |k,v| v.blank? }
  end
end
Run Code Online (Sandbox Code Playgroud)

更新2:这可以:all_blank像我期望的那样工作,但是它很丑陋,并且没有经过良好的测试。

module ActiveRecord::NestedAttributes::ClassMethods
  REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |k, v| k == '_destroy' || v.valueless? } }
end
class Object
  alias_method :valueless?, :blank?
end
class Hash
  def valueless?
    blank? || all? { |k, v| v.valueless? }
  end
end
Run Code Online (Sandbox Code Playgroud)

更新3: h!更新1中有错字。这个版本似乎确实有效。

class Hash
  def blank?
    empty? || all? { |k,v| v.blank? }
  end
end
Run Code Online (Sandbox Code Playgroud)

这是否有太多可能导致意想不到的后果成为可行的选择?如果这是一个不错的选择,那么此代码应放在我的应用程序中的什么位置?

Sia*_*Sia 0

这在 Rails 4.2.4 中仍然是一个问题,所以我想我会分享我所学到的东西。要促进在 Rails 中进行修复,请参阅此问题和此拉取请求

我的修复基于该拉取请求。在你的情况下,它看起来像这样(为了清楚起见,三个点只是为了跳过其他代码):

class Book < ActiveRecord::Base
  ...
  accepts_nested_attributes_for :parts, 
    reject_if: proc { |attributes| deep_blank?(attributes) }
  ...
    def self.deep_blank?(hash)
    hash.each do |key, value|
      next if key == '_destroy'
      any_blank = value.is_a?(Hash) ? deep_blank?(value) : value.blank?
      return false unless any_blank
    end
    true
  end
end
Run Code Online (Sandbox Code Playgroud)