Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
693 views
in Technique[技术] by (71.8m points)

ruby on rails - Destroy on blank nested attribute

I would like to destroy a nested model if its attributes are blanked out in the form for the parent model - however, it appears that the ActiveRecord::Callbacks are not called if the model is blank.

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

and

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

I have a form for Artist which uses fields_for to show the fields for the artist's associated tour dates, which works for editing and adding new tour dates, but if I merely blank out a tour date (to delete it), destroy_if_blank is never called. Presumably the Artist controller's @artist.update_attributes(params[:artist]) line doesn't consider a blank entity worth updating.

Am I missing something? Is there a way around this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I would keep the :reject_if block but insert :_destroy => 1 into the attributes hash if your conditions are met. (This is useful in the cases where it's not convenient to add _destroy to the form code.)

You have to do an extra check to see if the record exists in order to return the right value but the following seems to work in all cases for me.

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

You could apply when all attributes are blank by just changing the empty calculation to:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...