How to Use has_and_belongs_to_many in Ruby on Rails
In Ruby on Rails, use
has_and_belongs_to_many to create a many-to-many association between two models without an intermediate model. You define this in both models and create a join table with no primary key to link them.Syntax
The has_and_belongs_to_many association is declared inside both models to link them directly through a join table. The join table name is the plural names of both models in alphabetical order, separated by an underscore.
- Model A:
has_and_belongs_to_many :model_bs - Model B:
has_and_belongs_to_many :model_as - Join table: named
model_as_model_bswith columnsmodel_a_idandmodel_b_id
ruby
class ModelA < ApplicationRecord has_and_belongs_to_many :model_bs end class ModelB < ApplicationRecord has_and_belongs_to_many :model_as end
Example
This example shows two models, Author and Book, linked with has_and_belongs_to_many. Authors can write many books, and books can have many authors.
ruby
class Author < ApplicationRecord has_and_belongs_to_many :books end class Book < ApplicationRecord has_and_belongs_to_many :authors end # Migration to create join table class CreateAuthorsBooksJoinTable < ActiveRecord::Migration[7.0] def change create_join_table :authors, :books do |t| t.index :author_id t.index :book_id end end end # Usage in Rails console # author = Author.create(name: "Alice") # book = Book.create(title: "Rails Guide") # author.books << book # author.books # returns [book] # book.authors # returns [author]
Output
author.books returns an array of associated books; book.authors returns an array of associated authors
Common Pitfalls
- Not creating the join table with the correct name and columns causes errors.
- Using
has_many :throughis preferred if you need extra attributes on the join. - Forgetting to declare
has_and_belongs_to_manyin both models breaks the association. - Join table should not have a primary key column.
ruby
class User < ApplicationRecord # Wrong: declared only in one model has_and_belongs_to_many :groups end class Group < ApplicationRecord # Missing declaration end # Correct way: class User < ApplicationRecord has_and_belongs_to_many :groups end class Group < ApplicationRecord has_and_belongs_to_many :users end
Quick Reference
- Use when: simple many-to-many without extra data on join.
- Join table: plural model names in alphabetical order, no id column.
- Declare in both models:
has_and_belongs_to_many :other_model. - Migration: use
create_join_table :model_as, :model_bs.
Key Takeaways
Use has_and_belongs_to_many in both models to set up many-to-many without extra join data.
Create a join table named with both model names in alphabetical order without a primary key.
Declare the association in both models to enable bidirectional linking.
Use create_join_table migration helper to generate the join table easily.
Switch to has_many :through if you need to store extra information on the join.