销毁空白嵌套属性

时间:2012-06-08 04:18:45

标签: ruby-on-rails activerecord

我想销毁一个嵌套模型,如果它的属性在父模型的表单中被消隐了 - 但是,如果模型是空白的话,似乎不会调用ActiveRecord::Callbacks

class Artist < ActiveRecord::Base
  using_access_control
  attr_accessible :bio, :name, :tour_dates_attributes
  has_many :tour_dates, :dependent => :destroy
  accepts_nested_attributes_for :tour_dates, :reject_if => lambda { |a| a[:when].blank? || a[:where].blank? }, :allow_destroy => true
  validates :bio, :name :presence => true

  def to_param
    name
  end
end

class TourDate < ActiveRecord::Base
  validates :address, :when, :where, :artist_id, :presence => true
  attr_accessible :address, :artist_id, :when, :where
  belongs_to :artist
  before_save :destroy_if_blank

  private
  def destroy_if_blank
    logger.info "destroy_if_blank called"
  end
end

我有一个艺术家的表格,使用fields_for来显示艺术家相关旅行日期的字段,这些字段用于编辑和添加新的旅行日期,但是如果我只是删除一个旅行日期(删除它) ),永远不会调用destroy_if_blank。据推测,Artist控制器的@artist.update_attributes(params[:artist])行不会考虑值得更新的空白实体。

我错过了什么吗?有办法解决这个问题吗?

5 个答案:

答案 0 :(得分:68)

我会保留:reject_if块,但是插入:_destroy =&gt;如果满足您的条件,则进入属性哈希值1。 (这在将_destroy添加到表单代码不方便的情况下很有用。)

你必须做一个额外的检查以查看记录是否存在以便返回正确的值,但以下似乎适用于我。

accepts_nested_attributes_for :tour_dates, :reject_if => :reject_tour, :allow_destroy => true

def reject_tour(attributes)
  exists = attributes['id'].present?
  empty = attributes.slice(:when, :where).values.all?(&:blank?)
  attributes.merge!({:_destroy => 1}) if exists and empty # destroy empty tour
  return (!exists and empty) # reject empty attributes
end

只需将empty计算更改为

,即可在所有属性为空时应用

empty = attributes.except(:id).values.all?(&:blank?)

答案 1 :(得分:6)

如果'where'或'when'为空白,则您有代码表示应忽略该记录,在accepts_nested _attributes行上删除reject_if并且可能会调用您的destroy_if空白。

通常要销毁,您可以在嵌套记录上设置_destroy属性,查看文档http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

另外,今天只使用了一些茧,并认为它很棒,https://github.com/nathanvda/cocoon

答案 2 :(得分:6)

我今天设法做了这样的事情。就像@shuriu说的那样,你最好的选择就是删除reject_if选项并自己处理破坏。 mark_for_destruction派上用场:

class Artist < ActiveRecord::Base
  accepts_nested_attributes_for :tour_dates

  before_validation :mark_tour_dates_for_destruction 

  def mark_tour_dates_for_destruction
    tour_dates.each do |tour_date|
      if tour_date.when.blank? or tour_date.where.blank?
        tour_date.mark_for_destruction
      end
    end
  end
end

答案 3 :(得分:3)

与Steve Kenworthy的答案类似,没有局部变量。

 accepts_nested_attributes_for :tour_dates, :reject_if => :reject_tour, :allow_destroy => true

 def reject_tour(attributes)
    if attributes[:when].blank? || attributes[:where].blank?
      if attributes[:id].present?
        attributes.merge!({:_destroy => 1}) && false
      else
        true
      end
    end
  end

答案 4 :(得分:1)

由于传递给reject_if的{​​{1}}选项,目前的代码无法使用。

正如基督莫尔所说,最简单的方法是在更新父项时为嵌套模型设置accepts_nested_attributes_for属性,并且将销毁嵌套模型。有关详细信息,请参阅文档,或this railscast

或者您可以使用像cocoon或者awesome_nested_fields这样的宝石。

要具体执行您想要的操作,您应该删除_destroy选项,并在父对象内的回调中处理逻辑。它应该检查tour_dates_attributes中的空白值并销毁嵌套模型。但要谨慎行事......