Before_destroy does not fire from update_attributes

I have a student who has many courses. In action and # student update form, I accept the course_ids list. When this list changes, I would like to call a specific function. The code that I have is called if update_attributes creates a student course, but it does not call if update_attributes destroys a student course. Can I make it shoot, or do I need to detect the changes myself?

# app/models/student.rb class Student < ActiveRecord::Base belongs_to :teacher has_many :grades has_many :course_students, :dependent => :destroy has_many :courses, :through => :course_students has_many :course_efforts, :through => :course_efforts # Uncommenting this line has no effect: #accepts_nested_attributes_for :course_students, :allow_destroy => true #attr_accessible :first_name, :last_name, :email, :course_students_attributes validates_presence_of :first_name, :last_name ... end # app/models/course_student.rb class CourseStudent < ActiveRecord::Base after_create :reseed_queues before_destroy :reseed_queues belongs_to :course belongs_to :student private def reseed_queues logger.debug "******** attempting to reseed queues" self.course.course_efforts.each do |ce| ce.reseed end end end # app/controllers/students_controller.rb def update params[:student][:course_ids] ||= [] respond_to do |format| if @student.update_attributes(params[:student]) format.html { redirect_to(@student, :notice => 'Student was successfully updated.') } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @student.errors, :status => :unprocessable_entity } end end end 
+7
collections callback ruby-on-rails associations
source share
6 answers

It turns out that this behavior is documented directly using the has_many method. From the API documentation:

collection = objects Replaces the contents of collections by deleting and adding objects as needed. If the: through option is true callbacks in union models, they are triggered, except for the destruction of callbacks, since deletion is direct.

I am not sure what β€œsince deletion is direct” means, but there it is.

+7
source share

Accepts nested attributes, a flag is required to trigger nested destruction. At least it happened when.

+1
source share

If you add dependent: :destroy , it will honor this. Please note: if you use has_many through: you need to add this option for both.

  has_many :direct_associations, dependent: :destroy, autosave: true has_many :indirect_associations, through: :direct_associations, dependent: :destroy 

(I used this in Rails 3, I'm sure it will work on Rails 4 too)

+1
source share

When an entry is deleted using update / update_attributes, it invokes the delete method instead of destroy .

@student.update_attributes(params[:student])

delete method skips callbacks, and therefore after_create / before_destroy will not be called. Instead, you can use accepts_nested_attributes_for , which deletes a record and also supports callbacks.

accepts_nested_attributes_for :courses, allow_destroy: true

@student.update_attributes(courses_attributes: [ {id: student_course_association_id, _destroy: 1 } ])

+1
source share

If your CourseStudent specify belongs_to :student, :dependent => :destroy , it seems that the CourseStudent entry would not be valid without Student involvement.

Trying to follow the LH discussion that I linked to above, and this one , I would also try to translate the before_destroy to CourseStudent below belongs_to . A related example demonstrates how the order of callbacks is after_create , perhaps the same applies to before_destroy . And of course, since you are using Edge Rails, I would also try RC, a bug might have been fixed.

Otherwise, I will try to create a really simple Rails application with two models that demonstrate the problem and submit it to Lighthouse Rails.

0
source share

Failed to leave a comment, so I will just add a response entry.

Just ran into the same error. After a few hours, trying to understand this issue and about google-ing hour, I accidentally stumbled upon this question. Along with the associated LH ticket and quote from the API, this now makes sense. Thanks!

While searching on Google, I found an old ticket. The direct link does not work, but the Google cache has a copy. Just check Google for the cached version of dev.rubyonrails.org/ticket/7743

It seems that the patch never got into Rails.

0
source share

All Articles