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 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 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 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 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 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 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 : api.rubyonrails.com/classes/ActiveRecord/Validations.html#M000931 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 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 : > api.rubyonrails.com/classes/ActiveRecord/Validations.html#M000931 > > > Alain Ravet > -------- > blog.ravet.com-- Posted via 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 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 -------- 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 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 > -------- > 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 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 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 -------- 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 groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---