I have a student that has many courses. In the student#update action and form, I accept a list of course_ids. When that list changes, I'd like to call a certain function. The code I have does get called if the update_attributes creates a course_student, but does not get called if the update_attributes destroys a course_student. Can I get this to fire, or do I have 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
It turns out that this behavior is documented, right on the has_many method. From the API documentation:
collection=objects Replaces the collections content by deleting and adding objects as appropriate. If the :through option is true callbacks in the join models are triggered except destroy callbacks, since deletion is direct.
I'm not sure what "since deletion is direct" means, but there we have it.
When a record is deleted using update/update_attributes, it fires delete
method instead of destroy
.
@student.update_attributes(params[:student])
Delete
method skips callbacks and therefore after_create
/ before_destroy
would not be called.
Instead of this accepts_nested_attributes_for can be used which deletes the record and also support callbacks.
accepts_nested_attributes_for :courses, allow_destroy: true
@student.update_attributes(courses_attributes: [ {id: student_course_association_id, _destroy: 1 } ])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With