5

我的模型设置如下。一切正常,除了允许空白部分记录,即使所有部分和章节字段都是空白的。

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, value| key == '_destroy' || value.blank? } }. 所以,我使用它而不是:all_blank添加一些调试。看起来正在发生的事情是部件的 chapters 属性正在响应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:我尝试重新定义blank?Hash但这会导致问题。

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

这是否有太多可能导致意外后果成为可行的选择?如果这是一个不错的选择,那么这段代码应该在我的应用程序中的哪个位置存在?

4

3 回答 3

3

使用:all_blankwith 时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?
于 2018-02-06T16:03:35.427 回答
2

在这种情况下,先前答案中的方法将不起作用 -

book = Book.create({ 
  parts_attributes: { 
    name: '',
    chapters_attributes: {[
      { name: '', _destroy: false}, 
      { id: '', name: '', _destroy: false }
    ]} 
  } 
})

在这里,我们为章节提供了一个空白值数组,其中包含空白或 _destroy 字段。如果您也需要拒绝这些值,那么您可以使用此方法 -

 def all_blank?(attributes)
   attributes.all? do |key, value|
     key == '_destroy' || value.blank? ||
     value.is_a?(Hash) && all_blank?(value) ||
     value.is_a?(Array) && value.all? { |val| all_blank?(val) }
   end
 end

这里除了前面的条件外,我们还添加了一行,它检查数组中的所有元素是否为空,然后也拒绝它。

于 2020-07-20T19:39:09.883 回答
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
于 2016-01-22T15:08:53.533 回答