簡體   English   中英

當使用雙重嵌套關聯時,reject_if: :all_blank for 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_blankaccepts_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM