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 my case, I intend to keep my application small and simple.

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

Popular posts from this blog

Generating INSERT statement from SELECT using SQuirreL SQL

OneNote: We need the password to sync this notebook (Error code: 0xE4010643)

Fixing the AKE BC398 USB3.0 sluggish performance