[英]Rails how to reject_if when accepts_nested_attributes_for
[英]How does reject_if: :all_blank for accepts_nested_attributes_for work when working with doubly nested associations?
我的模型設置如下。 一切正常,除了允許空白部分記錄,即使所有部分和章節字段都是空白的。
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? } }
proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.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
像我預期的: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:哦! 更新 1 中有一個錯字。 這個版本好像可以用。
class Hash
def blank?
empty? || all? { |k,v| v.blank? }
end
end
這是否有太多可能導致意外后果成為可行的選擇? 如果這是一個不錯的選擇,那么這段代碼應該放在我的應用程序中的哪個位置?
當:all_blank
與accepts_nested_attributes_for
:all_blank
使用時,它將檢查每個單獨的屬性以查看它是否為空。
# 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?
在這種情況下,先前答案中的方法將不起作用 -
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
這里除了前面的條件之外,我們還添加了一行,檢查數組中的所有元素是否為空,然后也拒絕它。
這仍然是 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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.