我的模型设置如下。一切正常,但即使所有零件和章节字段都是空白,也允许使用空白部分记录。
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
并添加一些调试。看起来正在发生的事情是该部分的章节属性是使用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
这是否有太大的潜在意外后果成为可行的选择?如果这是一个不错的选择,那么在我的应用程序中该代码应该存在吗?
答案 0 :(得分:1)
在这种情况下,先前答案中的方法将不起作用-
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
除了前面的条件外,我们还添加了一行,用于检查数组中的所有元素是否为空,然后也将其拒绝。
答案 1 :(得分:0)
这仍然是Rails 4.2.4中的一个问题,所以我想我会分享我学到的东西。要在Rails中推广修复,请参阅此issue和此pull request。
我的解决方案是基于拉取请求。在你的情况下,它看起来像这样(只是要清楚,三个点只是跳过其他代码):
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