In Rails, there are two main ways of creating many-to-many relationships between models:
- using a has_and_belongs_to_many association
- using has_many :through association
This is Quick tips, so let's cut to the chase.
Using has_and_belongs_to_many
This is the easiest way to establish a many-to-many relationship. It will create a database table to relate the two models (but not a third model to represent the relation).
Suppose you have two models that need to have a many-to-many relationship:
- instruments
- musicians
The first step is to write a migration, that (for this example) would look like this:
class AddHABTMForInstrumentsAndmusicians < ActiveRecord::Migration[6.0]
def change
create_table :instruments_musicians, id: false do |t|
t.belongs_to :instrument
t.belongs_to :musician
end
end
end
If you run this migration it will generate the following table description in schema.rb:
create_table "instruments_musicians", id: false, force: :cascade do |t|
t.bigint "instrument_id"
t.bigint "musician_id"
t.index ["instrument_id"], name: "index_instruments_musicians_on_instrument_id"
t.index ["musician_id"], name: "index_instruments_musicians_on_musician_id"
end
Now the only thing you need is to add the has_and_belongs_to_many line to each model, like this:
class Instrument < ApplicationRecord
has_and_belongs_to_many :musicians
end
class Musician < ApplicationRecord
has_and_belongs_to_many :instruments
end
And we are done, that's all you need to create a has_and_belongs_to_many relationship.
Using has_many :through
The main difference between this option and the first one is that now a third model is created. Such model can contain things like custom behavior or validations, and it might be useful in many cases.
Going back to our example, for creating a has_many :through association we need to write a migration that creates a model that logically ties musicians and instruments, something like a songbook:
create_table :songbooks do |t|
t.belongs_to :instrument
t.belongs_to :musician
t.string :title #we can add extra attributes if we want
t.timestamps
end
Now, we can add the associations to the models:
class Songbook < ApplicationRecord
belongs_to :instrument
belongs_to :musician
end
class Instrument < ApplicationRecord
has_many :songbooks
has_many :musicians, through: :songbooks
end
class musician < ApplicationRecord
has_many :songbooks
has_many :instruments, through: :songbooks
end
And that's how you create a has_many :through association.
Which one should I use?
My rule of thumb is the following: If I need validations or any form of custom behavior in the relationship, I go with has_many :through. Otherwise (most cases) I go with has_and_belongs_to_many.
Thank you for reading!
What to do next
- Share this article with friends and colleagues. Thank you for helping me reach people who might find this information useful.
- You can check the official Rails documentation for associations here.
- If you want to learn more about Rails you can check RoR Tutorial. This and other very helpful books can be found in the recommended reading list.
- Send me an email with questions, comments or suggestions (it's in the About Me page)