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

end

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 = StudentsSchool.new(:school_id => 1, :student_id => 10)
=> #…

ss.student
=> #…

ss.student.students_school
=> []

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!

-Danimal

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.

John