has_many Association
has_many is the most common association in Rails. It indicates a one-to-many relationship where one model has multiple instances of another model.
Basic has_many
ruby
1class User < ApplicationRecord
2 has_many :articles
3end
4
5class Article < ApplicationRecord
6 belongs_to :user
7end
8
9# Migration
10class CreateArticles < ActiveRecord::Migration[7.1]
11 def change
12 create_table :articles do |t|
13 t.string :title
14 t.text :body
15 t.references :user, null: false, foreign_key: true
16 t.timestamps
17 end
18 end
19endUsing has_many
Reading Associated Records
ruby
1user = User.find(1)
2
3# Get all articles
4user.articles # => Collection of articles
5user.articles.count # => 5
6user.articles.size # => 5 (uses counter_cache if available)
7user.articles.length # => 5 (loads all records)
8user.articles.empty? # => false
9user.articles.any? # => true
10
11# Access specific articles
12user.articles.first # => First article
13user.articles.last # => Last article
14user.articles.find(5) # => Article with id 5
15user.articles[0] # => First article (loads all!)
16
17# Chain with scopes and conditions
18user.articles.published
19user.articles.where(published: true)
20user.articles.order(created_at: :desc)
21user.articles.limit(10)Creating Associated Records
ruby
1user = User.find(1)
2
3# Build (doesn't save)
4article = user.articles.build(title: "New Article")
5article.user_id # => 1 (automatically set)
6article.save
7
8# Create (saves immediately)
9article = user.articles.create(title: "New Article")
10
11# Create with bang (raises on failure)
12article = user.articles.create!(title: "New Article")
13
14# Create multiple
15user.articles.create([
16 { title: "Article 1" },
17 { title: "Article 2" }
18])Modifying Associations
ruby
1user = User.find(1)
2
3# Add existing articles
4user.articles << Article.find(5)
5user.articles.push(Article.find(6))
6
7# Replace all articles
8user.articles = [Article.find(1), Article.find(2)]
9
10# Remove article (sets foreign key to nil)
11user.articles.delete(Article.find(5))
12
13# Remove all articles
14user.articles.clear
15
16# Destroy articles (also deletes from database)
17user.articles.destroy(Article.find(5))
18user.articles.destroy_all
19
20# Check if exists
21user.articles.exists?
22user.articles.exists?(5)
23user.articles.include?(some_article)has_many Options
Class Name and Foreign Key
ruby
1class User < ApplicationRecord
2 # Different class name
3 has_many :posts, class_name: "Article"
4
5 # Different foreign key
6 has_many :articles, foreign_key: "author_id"
7
8 # Both
9 has_many :written_articles, class_name: "Article", foreign_key: "author_id"
10endDependent Option
What happens to associated records when the parent is destroyed:
ruby
1class User < ApplicationRecord
2 # Destroy all articles when user is destroyed
3 has_many :articles, dependent: :destroy
4
5 # Delete without callbacks
6 has_many :articles, dependent: :delete_all
7
8 # Set foreign key to null
9 has_many :articles, dependent: :nullify
10
11 # Raise error if any exist
12 has_many :articles, dependent: :restrict_with_exception
13
14 # Add error to parent, prevent deletion
15 has_many :articles, dependent: :restrict_with_error
16end
17
18user.destroy # Also destroys all user.articlesOrdering and Conditions
ruby
1class User < ApplicationRecord
2 # Default ordering
3 has_many :articles, -> { order(created_at: :desc) }
4
5 # With conditions
6 has_many :published_articles, -> { where(published: true) }, class_name: "Article"
7 has_many :draft_articles, -> { where(published: false) }, class_name: "Article"
8
9 # Multiple conditions
10 has_many :recent_published_articles, -> {
11 where(published: true).order(created_at: :desc).limit(5)
12 }, class_name: "Article"
13
14 # With scope parameter
15 has_many :articles_after, ->(date) { where("created_at > ?", date) }, class_name: "Article"
16end
17
18user.recent_published_articles
19user.articles_after(1.week.ago)Counter Cache
ruby
1class User < ApplicationRecord
2 has_many :articles
3end
4
5class Article < ApplicationRecord
6 belongs_to :user, counter_cache: true
7end
8
9# Migration
10add_column :users, :articles_count, :integer, default: 0
11
12# Now you can use:
13user.articles_count # No database query!
14user.articles.size # Uses counter cache automaticallyInverse Of
Helps Rails maintain consistency between associated objects:
ruby
1class User < ApplicationRecord
2 has_many :articles, inverse_of: :user
3end
4
5class Article < ApplicationRecord
6 belongs_to :user, inverse_of: :articles
7end
8
9user = User.first
10article = user.articles.first
11
12user.name = "New Name"
13article.user.name # => "New Name" (same object in memory)Collection Methods
has_many adds many helpful methods:
ruby
1user.articles # All articles
2user.articles=(articles) # Replace all
3user.article_ids # Array of IDs
4user.article_ids=(ids) # Replace by IDs
5user.articles.build # Build new
6user.articles.create # Create new
7user.articles.create! # Create new with exception
8user.articles.reload # Reload from database
9user.articles.size # Count
10user.articles.length # Count (loads all)
11user.articles.count # SQL COUNT
12user.articles.empty? # Check if empty
13user.articles.any? # Check if any
14user.articles.many? # More than one?
15user.articles.include?(a) # Check membership
16user.articles.exists? # Any exist?
17user.articles.distinct # Unique records
18user.articles.reset # Reset cached collection
19user.articles.find(...) # Find within collection
20user.articles.where(...) # Filter collection
21user.articles.destroy_all # Destroy all
22user.articles.delete_all # Delete all
23user.articles << article # Add to collection
24user.articles.delete(a) # Remove from collection
25user.articles.destroy(a) # Destroy from collection
26user.articles.clear # Remove allPreloading Associations
Avoid N+1 queries:
ruby
1# N+1 problem (bad)
2users = User.all
3users.each do |user|
4 puts user.articles.count # Query for each user!
5end
6
7# Eager loading (good)
8users = User.includes(:articles)
9users.each do |user|
10 puts user.articles.size # No additional queries
11end
12
13# Preload (similar to includes)
14users = User.preload(:articles)
15
16# Eager load (uses LEFT OUTER JOIN)
17users = User.eager_load(:articles)Scopes on Associations
ruby
1class User < ApplicationRecord
2 has_many :articles do
3 def published
4 where(published: true)
5 end
6
7 def drafts
8 where(published: false)
9 end
10
11 def find_by_title(title)
12 find_by(title: title)
13 end
14 end
15end
16
17user.articles.published
18user.articles.drafts
19user.articles.find_by_title("Hello World")The has_many association is fundamental to building relationships in Rails applications!
