35

如果嵌套模型的属性在父模型的表单中被空白,我想销毁嵌套模型 - 但是,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])行不认为值得更新的空白实体。

我错过了什么吗?有没有解决的办法?

4

5 回答 5

79

如果满足您的条件,我会保留 :reject_if 块,但将 :_destroy => 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?)
于 2012-12-08T03:47:14.093 回答
9

我今天设法做这样的事情。就像@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? || tour_date.where.blank?
        tour_date.mark_for_destruction
      end
    end
  end
end
于 2012-10-02T17:46:20.887 回答
6

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

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

另外,今天刚用 cocoon 做了一些,觉得很棒,https://github.com/nathanvda/cocoon

于 2012-06-08T04:29:11.333 回答
5

类似于史蒂夫肯沃西的回答,没有局部变量。

 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
于 2014-12-06T23:42:47.780 回答
1

使用您当前的代码,这是不可能的,因为reject_if传递给accepts_nested_attributes_for.

正如 Christ Mohr 所说,最简单的方法是_destroy在更新 parent 时为嵌套模型设置属性,嵌套模型将被销毁。有关此内容或此 railscast的更多信息,请参阅文档。

或者,您可以使用 cocoon 或 awesome_nested_fields 之类的 gem。

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

于 2012-06-08T07:38:57.200 回答