How to use ActiveRecord has_and_belongs_to_many (HABTM) association in Rails

When building an application in Rails, often when we need to create relationship between objects, we'll try look for the correct association that we can use based on our own use case. One of the association is has_and_belongs_to_many, a.k.a HABTM (yep, that acronym exists!). While many articles talks about why one should never use HABTM in place of the has_many..., but I think there's a place for HABTM, in keeping the application small and simple might be one of it.

Let's assume I have a 2 models, "Programmer" and "Project", a programmer can work on many projects, and a project can have many programmers (obviously right?).

To do this in Rails, I took the following steps.

Generate the migration file
rails g migration CreateJoinTableProgrammerProject programmer project




This would generate a migration file like the following:
class CreateJoinTableProgrammerProject < ActiveRecord::Migration
def change
create_join_table :programmers, :projects do |t|
# t.index [:programmer_id, :project_id]
# t.index [:project_id, :programmer_id]
end
end
end

Uncomment the first line that create the index for [:programmer_id, :project_id]; this will let rails to add the index in the database (choosing the right index depend on how the data are query).



Once the migration file has been configured, run migration to update the schema.rb



rake db:migrate


You'd also need to update the model classes to includes the necessary relations.


# models/programmer.rb
class Programmer < ActiveRecord::Base
has_and_belongs_to_many :projects
end

# models/project.rb
class Project < ActiveRecord::Base
has_and_belongs_to_many :programmers
end


Done, a many-to-many has been created relationships between "Programmer" and "Project". If you look under the hood, the migration actually created a joining table called programmers_projects with only two field, the programmer_id and the project_id, and this table have no corresponding model class in the project.


To associate a "Programmer" with a "Project", the codes can be written as follow:


# some_controller.rb

@project = programmer.projects.build(project_params)
programmer.save


Important: Notice that the save are called from the programmer object, not the @project object. If you call save from the project, the relationship record won’t be added to the join table.

Comments