has_many - edit the association

Stephan,

  > I have a has_and_belongs_to_many relationship, let's say   > class Student < ActiveRecord::Base   > has_and_belongs_to_many :courses   > end   > class Course < ActiveRecord::Base   > has_and_belongs_to_many :students   > end

CRUDify/complexify your models, and your life/code will be simpler: add 1 model - Enrolment - and use has_many :through instead of hbtm

Something like (untested) :

  class Student       has_many :enrolments , :dependent => :destroy       has_many :courses , :through => :enrolments   end   class Course       has_many :enrolments , :dependent => :destroy       has_many :studends , :through => :enrolments   end   class Enrolment      belongs_to :course      belongs_to :studen   end

You would then create enrolments explicitely

    Enrolment.create(:student => a_student, :course => a_course)

(you'd place the validations rules in the enrolment model).

For more about this CRUDification, see slides 24 and following of "World of resources", by DHH

   http://media.rubyonrails.org/presentations/worldofresources.pdf

Alain Ravet

Stephan

  > Thanks - sounds good. What would the controller's update method   > look like then?

By creating a new model - Enrolment - you simplify your controller by having the model do all the - model - validation.

  > Enrolment.create will manipulate the database, while I want to first   > perform validations (must have at least one 4-credit course, for   > example), before the database-saves.

Something like (untested) :

  class Enrolment < ActiveRecord::Base      belongs_to :course      belongs_to :student

  protected      def validate         errors.add("student", "does not have enough credits") unless student.credits >= 4      end   end

API validations : http://api.rubyonrails.com/classes/ActiveRecord/Validations.html#M000931

Alain Ravet

Stephan

My code was just an example; adapt for your needs.

   > The first "Enrolment.create" might fail, even if the parameters    > submitted from the form contain a 4-credits course (unless the    > controller does some sorting, which doesn't look proper either).

I don't understand :

  class Enrolment < ActiveRecord::Base     ..     def validate       errors.add("student", "does not have enough credits") unless student.credits >= 4     end   end

will only fail if student.credits < 4

In the controller you'd have something like

  def register     student = Student.find(params[:student_id])     course = Course .find(params[:course_id])

    if Enrolment.create(student, course)        ..     else        flash[:notice] = "student cannot be enrolled for this course"        ..     end   end

Alain Ravet

Stephan,

  > In the form there are checkboxes for a whole bunch of courses.   > So there would be a whole sequence of   >   > Enrolment.create(student, course1) # course1 was checked off.   > Enrolment.create(student, course2) # course2 was checked off.   > Enrolment.create(student, course3) # course3 was checked off.   >   > But validation depends on the whole set (does one of them have 4   > credits).

I guess you have two options : - check the form values in the controller before creating any new record (the validation logic could be moved to the model, in a class method.), or - add a more complex (multiple) creator in the model.

Alain Ravet