Scopes & Query Interface
Name and manage reusable queries
Scopes name and define frequently used query conditions on models.
class Post < ApplicationRecord
scope :published, -> { where(status: 'published') }
scope :recent, -> { order(created_at: :desc) }
scope :by_author, ->(user) { where(user: user) }
scope :this_month, -> { where(created_at: Time.current.beginning_of_month..) }
end
Usage:
Post.published # only published posts
Post.published.recent # chaining
Post.published.by_author(user).recent # combining multiple scopes
Post.published.count # combine with aggregation
Query Interface key methods:
whereโ conditions (AND)orโ OR conditionsnotโ NOT conditionsorderโ sortinglimit/offsetโ pagingincludesโ Eager Loadingjoinsโ INNER JOINleft_joinsโ LEFT OUTER JOINselect/pluckโ select specific columns
Queries use Lazy Loading โ SQL only executes when data is actually needed.
Key Points
Define scope with scope :name, -> { where(...) }
Call with Model.scope_name (works like class method)
Chain multiple scopes for complex queries
Scope with args: scope :by_status, ->(s) { where(status: s) }
default_scope auto-applies to all queries (use with caution)
Lazy Loading: SQL executes when .to_a, .each, .count etc. are called
Pros
- ✓ Code reuse (DRY)
- ✓ Improved readability โ query intent is clear
- ✓ Flexible composition through chaining
- ✓ Lazy Loading prevents unnecessary queries
Cons
- ✗ default_scope causes unexpected behavior
- ✗ Too many scopes make code hard to trace
- ✗ Complex queries may be clearer in raw SQL
- ✗ Watch for side effects in scopes