Musings of an agile development manager and occasional code monkey

Validating HABTM relationships with Rails 3.x

There comes a time as you build up a rails application that you end up using the has_and_belongs_to_many (HABTM) macro. This macro is an easy way to create a many-to-many relationship between two of your ActiveRecord models.

In some cases you may want to validate that association. However, the traditional methods for validating rails models do not work.

The unit tests below described how I wanted the relationship to function.

classProjectTest<ActiveSupport::TestCasesetupdo@project=Project.new()endtest"may have many developers"do4.times{@project.developers<<FactoryGirl.create(:developer)}assert@project.saveendtest"must have at least one developer"do@project.saveassert_equal1,@project.errors.countassert_not_nil@project.errors[:developers]endend

In my case, I was hoping to validate that each project had at least one developer associated to it. Initially, I coded my models to make the first test pass.

To make the second test pass, I tried to implement a custom active record validator.

classProject<ActiveRecord::Basehas_and_belongs_to_many:developersvalidate:minimum_number_of_developersprivatedefminimum_number_of_developerserrors.add(:developers,"must have at least on developer")ifdevelopers.count<1endend

This, however, does NOT work with HABTM relationships. The way that these relationships work is that the associated property is not available until after the record is saved.

To get around this, we can validate as part of the after_save callback. Validating here and returning false from the callback will rollback the entire transaction.

classProject<ActiveRecord::Basehas_and_belongs_to_many:developersafter_save:validate_minimum_number_of_developersprivatedefvalidate_minimum_number_of_developersifdevelopers.count<1errors.add(:developers,"must have at least on developer")returnfalseendendend