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
I managed to do something like this today. Like @shuriu says, your best option is to remove the reject_if
option and handle destruction yourself. mark_for_destruction
comes in handy :
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
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?)
With your current code it's not possible, because of the reject_if
option passed to accepts_nested_attributes_for
.
As Christ Mohr said, the easiest way is to set the _destroy
attribute for the nested model when updating the parent, and the nested model will be destroyed. Refer to the docs for more info on this, or this railscast.
Or you can use a gem like cocoon, or awesome_nested_fields.
To do specifically what you want, you should remove the reject_if
option, and handle the logic in a callback inside the parent object. It should check for blank values in the tour_dates_attributes and destroy the nested model. But tread carefully...
You have code that says the record should be ignored if the 'where' or the 'when' is blank, on the accepts_nested _attributes line, remove the reject_if
and your destroy_if blank will likely be called.
Typically to destroy, you would set a _destroy
attribute on the nested record, check out the docs http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Also, just used cocoon for some of this today, and thought it was awesome, https://github.com/nathanvda/cocoon
Similar to Steve Kenworthy's answer, no local variables.
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