Validating an ActiveRecord object and its has_many :through associations

Considering an object with several has_many :through => associations, what is the 'best' way to handle validations?

As an example:

class Student < ActiveRecord::Base

  # some attrbutes like   # :name   # :grade

  # relationships

  has_many :students_assignment, :dependent => :destroy   has_many :assignments, :through => :students_assignment

  has_many :students_class, :dependent => :destroy   has_many :classes, :through => :students_class

  has_many :students_extracurricular_activity, :dependent => :destroy   has_many :extracurricular_activities, :through => :students_extracurricular_activity

  has_many :students_school, :dependent => :destroy   has_many :students_schools, :through => :students_school


Suppose a student can't belong to a class and extracurricular activity that occur at the same time? I figure that this validation would be best handled in the Student class since the other case would require writing validations for both classes and eca's. Also, this is just an example. So, what if a student also can only take a :class through multiple :schools if they are of a certain :grade and only 5 students can belong to each eca?

I'm trying to illustrate a complex situation where the combination of multiple associations affect the validity. So either each association has to be validated, which means processing for the most part the same validations for all associations. For instance, checking a students eca's against the students classes (for timing conflicts) would be done twice, once from eca's and again from classes. i would like to handle this through a validates_associated so that the Student is validated whenever an association is changed or added. However, I can't see how this would work.

To illustrate:

ss = => 1, :student_id => 10)

=> #<StudentsSchooll>...


=> #<Student>...



So the ss is still a temporary object that won't be seen in the Student validation.

Can anyone share some ideas?

With this complexity, you are best off (IMNSHO) rolling your own validation code. In the Student model, just override the "validate" method:

def validate   .. do all your logic here, e.g. ..   errors.add("student", "this student can't be in that class and that activity") if XYZ end

Then, you set the errors hash (or array? I forget what data structure it is) if anything goes wrong.

I'd start by pseudo-coding it in plain english to be sure you've articulated your association constraints. That call is on the model, so you get all the association goodness, like if a student has_many activities, you can do: self.activities in that call to reference the activities association for the student instance being validated.

Remember, get the logic working first, then refactor later. I used to have a lot of trouble with this. I'd spend an hour mentally figuring out the "perfect" way to capture and solve a specific problem instead of doing "good enough" in 3 minutes, then refactoring the next day for 5 minutes.

Good luck!


Thanks Danimal.

I'm about to do the same. My main issue is figure out how to validate with associated objects that only exist in memory. I guess I have to actually attach them to the main (Student) object and then validate.