ActiveRecord: can objects be added to has_many: through a single batch operation?

Take the following simple object model, for example:

class Course
  has_many :enrollments
  has_many :students, :through => :enrollments, :after_add => :send_email

  def send_email(student)
    puts "Email Sent"
  end
end

class Enrollment
  belongs_to :course
  belongs_to :student
end

class Student
  has_many :enrollments
  has_many :courses, :through => :enrollments
end

      

I would like to execute send_email

after adding one or more students to the course, however after_add is triggered after each item is added to the students collection.

bill = Student.first
carl = Student.last

Course.first.students << [bill, carl]

      

Will output

Email Sent
Email Sent

      

How can I catch the SINGLE event after all items have been added to the collection?

Can I insert records into a table enrollments

in a single query for all students, rather than one student?

Does Rails have some kind of built-in mechanism, or do I need to write my own logic to catch the batch cleanup calls before send_email

?

Thanks for any information!

+2


source to share


3 answers


Adding the after_create callback in the registration model will result in the desired behavior.

With batch assignments in has_many:, entries are created in the registration table through relations if necessary. For every new entry, Rails will apply all the validations and callbacks in the connection model. If student enrollment in the course is marked in an entry in the application pooling table. This will then work regardless of how you add a student to the course.

This is the code to change the registration class

class Enrollment < ActiveRecord::Base
  belongs_to :course
  belongs_to :student
  after_create :send_email

  protected
    def send_email
      course.send_email(student)
    end
end

      



It will call the course send_email procedure while you create a registration record using Rails.

The following will send emails.

@student.courses << @course
@course.students << @student
Enrollment.create :student => @student, :course => @course

      

+2


source


I have checked and it doesn't seem like any of the built-in callbacks will do what you need to do. You might want to experiment with custom callback classes to make sure you can achieve what you need.

http://guides.rubyonrails.org/activerecord_validations_callbacks.html#callback-classes



Good luck :)

0


source


What about:

@last_student = Student.last

def send_email(student)
    put 'email sent' if student == @last_student
end

      

It might be a little bit useless to use an instance variable, but it should do the job.

0


source







All Articles