0
0
RailsHow-ToBeginner · 4 min read

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_bs with columns model_a_id and model_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 :through is preferred if you need extra attributes on the join.
  • Forgetting to declare has_and_belongs_to_many in 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.