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
Now I have a form for a student, with a checkbox for applicable courses.
How do I validate the courses checked against some conditions, so that
if validation fails, the new associations are not saved?
I try, for example, in the student controller''s update method
 @student.attributes = params[:student]
 # gather the course assignment from other parameters...
 # updated_courses = .....
 #
 @student.courses = updated_courses.collect { |u| @student.courses.build
( u.attributes ) }
 @successful = @student.save
But the courses for the student are then actually wiped out.
What is an easy way?
Thanks a lot,
Stephan
-- 
Posted via http://www.ruby-forum.com/.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---
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
--------
blog.ravet.com/
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---
> 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) >Thanks - sounds good. What would the controller''s update method look like then? More specific, how would the snippet I mentioned earlier look with this new Enrolment class: @student.attributes = params[:student] # gather the course assignment from other parameters... # updated_courses = ..... # @student.courses = updated_courses.collect { |u| @student.courses.build ( u.attributes ) } @successful = @student.save 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. Stephan> > > Alain Ravet > -------- > blog.ravet.com/-- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
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
--------
http://blog.ravet.com
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---
Alain Ravet wrote:> 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 > endThanks a lot. This looks to me like it might not allow adding the first course to the student set of courses, if it doesn''t satisfy the "student.credits>=4" condition. 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). Also, the Enrolment.create would ensue a database save independently of other form-value validations of the student''s fields.. Am I missing something? I looked through the validations section of ActiveRecord , but couldn''t find anything applicable. Thanks again. Stephan> API validations : > http://api.rubyonrails.com/classes/ActiveRecord/Validations.html#M000931 > > > Alain Ravet > -------- > http://blog.ravet.com-- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
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
--------
http://blog.ravet.com
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---
Alain Ravet wrote:> 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 > -------- > http://blog.ravet.comIn 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). Plus, if the last Enrolment.create fails, one would like the first one not to commit. I''m getting the impression ActiveRecord doesn''t quite go as far as what I''m looking for. I don''t quite understand why one can perform student.courses = [ .. an array of courses .. ] and this gets saved right away, without a chance to apply other changes to the student record for one-step-validation. Stephan -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
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 -------- http://blog.ravet.com --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---