Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 801
Christopher Avatar asked Jun 08 '12 04:06

Christopher


1 Answers

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?) 
like image 61
Steve Kenworthy Avatar answered Sep 21 '22 20:09

Steve Kenworthy