views:

83

answers:

2

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
A: 

Should your CourseStudent specify belongs_to :student, :dependent => :destroy, it seems like a CourseStudent record would not be valid without a Student.

Trying to follow the LH discussion I linked to above, and this one I'd also try moving the before_destroy callback in CourseStudent below the belongs_to. The linked example demonstrates how the order of callbacks matters with an after_create, perhaps the same applies to before_destroy. And of course, since you're using Edge Rails, I'd also try the RC, maybe there was a bug they fixed.

Failing those things, I'd try to make a really simple Rails app with two models that demonstrates the problem and post it to Rails' Lighthouse.

Andy Atkinson
In student.rb, I added :dependent => :destroy to the has_many :course_students, so if a student gets destroyed, so will the course_student, not the other way around (although it doesn't make sense for either to exist without the other).I also moved before_destroy to after :belongs_to, and the code behaves the same. I have not yet tried the RC, and I'm not quite using edge Rails. I'm using RC4 because Heroku supports it for the moment and I'd have to migrate a couple developers.I'm hopeful of getting a simple work-around before I have to detect which items get deleted myself.
Chris
I haven't used nested attributes much. Does the `Dirty` [module stuff](http://api.rubyonrails.org/classes/ActiveRecord/Dirty.html) work with nested attributes. I was thinking it might make your own detection of deleted associated records easier. Maybe something like Parent.child.changed? in a method that is called as a `before_save` that does the delete if necessary. Just reaching at this point though, not using Rails 3 yet, and haven't used nested attributes in over a year. :(
Andy Atkinson
Andy, I removed the accept_nested_attributes_for, since there were no real attributes in there; just the list of ids. The code behaves the same.
Chris
A: 

Accepts nested attributes requires a flag to trigger nested destroy. Atleast it did back when.

McClain Looney
Excellent catch, McClain. And I was hopeful, but changing to this:>>> class Student < ActiveRecord::Base ... accepts_nested_attributes_for :course_students, :allow_destroy => true<<<did not help. With or without allow_destroy, the record gets deleted from the database, and the callback does not happen.
Chris
Just for fun, since there aren't really any course_student attributes to accept (just the collection_ids=), I removed accepts_nested_attributes_for entirely. Same behavior.
Chris