ruby-on-rails – 在使用双重嵌套关联时,reject_if :: all_blank对accepts_nested_attributes_for如何工作?

我的模型设置如下.一切正常,但即使所有零件和章节字段都是空白,也允许空白部件记录.

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

探讨代码:all_blank被proc {| attributes |替换attributes.all? {|键,值| key ==’_destroy’|| value.blank? }.所以,我使用它代替:all_blank并添加一些调试.看起来正在发生的是该部分的章节属性是否响应空白? false,因为它是一个实例化的哈希对象,即使它包含的是另一个只包含空值的哈希:

chapters_attributes: !ruby/hash:ActionController::Parameters
  '0': !ruby/hash:ActionController::Parameters
    title: ''
    text: ''

难道这不是以这种方式工作吗?

我找到了一个解决方法:

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
}

但我希望我错过了一个更好的方法来处理这个问题.

更新1:我尝试重新定义空白?哈希,但这会引起问题.

class Hash
  def blank?
    :empty? || all? { |k,v| v.blank? }
  end
end

更新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

更新3:Doh!更新1中有一个拼写错误.这个版本似乎确实有效.

class Hash
  def blank?
    empty? || all? { |k,v| v.blank? }
  end
end

这是否有太大的潜在意外后果成为可行的选择?如果这是一个不错的选择,那么在我的应用程序中该代码应该存在吗?

最佳答案 当使用:all_blank和accepts_nested_attributes_for时,它将检查每个单独的属性以查看它是否为空.

# From the api documentation
REJECT_ALL_BLANK_PROC = proc do |attributes|
  attributes.all? { |key, value| key == "_destroy" || value.blank? }
end

http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

嵌套关联的属性将是包含关联属性的哈希.检查属性是否为空的检查将返回false,因为哈希不为空 – 它包含关联的每个属性的键.由于嵌套关联,此行为将导致reject_if :: all_blank返回false.

要解决此问题,您可以将自己的方法添加到application_record.rb,如下所示:

# Add an instance method to application_record.rb / active_record.rb
def all_blank?(attributes)
  attributes.all? do |key, value|
    key == '_destroy' || value.blank? ||
    value.is_a?(Hash) && all_blank?(value)
  end
end

# Then modify your model book.rb to call that method
accepts_nested_attributes_for :parts, reject_if: :all_blank?
点赞