ActiveRecord & Migrations
ActiveRecord is Rails's ORM — it maps classes to tables and objects to rows. Mastering associations, scopes, validations, and the query interface covers the full lifecycle of data in a Rails application.
Associations & Scopes
# app/models/user.rb
class User < ApplicationRecord
# Associations
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_one :profile, dependent: :destroy
has_many :likes
has_many :liked_posts, through: :likes, source: :post # many-to-many via join table
# Validations
validates :email, presence: true, uniqueness: { case_sensitive: false },
format: { with: URI::MailTo::EMAIL_REGEXP }
validates :username, presence: true, length: { minimum: 3, maximum: 30 },
format: { with: /A[a-z0-9_]+z/, message: "only lowercase letters, numbers, underscores" }
validates :age, numericality: { greater_than_or_equal_to: 18 }, allow_nil: true
# Callbacks
before_save :normalize_email
after_create :send_welcome_email
# Scopes — reusable query fragments
scope :active, -> { where(active: true) }
scope :admins, -> { where(role: :admin) }
scope :recent, -> { order(created_at: :desc) }
scope :by_country, ->(country) { where(country: country) }
private
def normalize_email
self.email = email.downcase.strip
end
def send_welcome_email
WelcomeMailer.with(user: self).welcome.deliver_later
end
end
# app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
has_many :taggings
has_many :tags, through: :taggings
validates :title, presence: true, length: { maximum: 255 }
validates :body, presence: true
scope :published, -> { where(published: true) }
scope :draft, -> { where(published: false) }
endQueries & Migrations
# ActiveRecord query interface
User.all # All users (returns ActiveRecord::Relation)
User.find(1) # By primary key (raises if not found)
User.find_by(email: "alice@example.com") # First match or nil
User.where(active: true).order(:name).limit(20)
User.where("created_at > ?", 1.week.ago)
User.where(role: [:admin, :moderator]) # IN (:admin, :moderator)
User.where.not(role: :banned)
# Eager loading (avoid N+1 queries)
# N+1: posts.each { |p| p.user.name } — fires 1 + N queries
Post.includes(:user, :tags).all # 3 queries total
Post.eager_load(:user).where(users: { active: true }) # JOIN — allows WHERE on association
Post.preload(:comments) # Always uses separate queries (no JOINs)
# Select, group, count, pluck
User.count
User.where(active: true).count
User.group(:country).count # { "US" => 120, "UK" => 45 }
User.pluck(:id, :email) # Returns raw array, not AR objects (fast)
User.select(:id, :email) # Returns AR objects with only those attrs
# update_all / delete_all (no callbacks or validations!)
Post.where(published: false).update_all(archived: true)
Post.where("created_at < ?", 6.months.ago).delete_all
# Transactions
ActiveRecord::Base.transaction do
account.update!(balance: account.balance - 100)
recipient.update!(balance: recipient.balance + 100)
end
# If either update fails, both are rolled back# Generate a migration
rails generate migration AddPublishedToArticles published:boolean
rails generate migration CreateProducts name:string price:decimal stock:integer
rails generate migration AddIndexToUsersEmail
# Migration file — db/migrate/20240115123456_add_published_to_articles.rb
# class AddPublishedToArticles < ActiveRecord::Migration[7.1]
# def change
# add_column :articles, :published, :boolean, default: false, null: false
# add_index :articles, :published
# add_index :articles, [:user_id, :published] # composite index
# add_column :articles, :published_at, :datetime
# remove_column :articles, :legacy_flag, :boolean # pass old type for reversibility
# end
# end
# Database commands
rails db:migrate # Run pending migrations
rails db:rollback # Roll back last migration
rails db:rollback STEP=3 # Roll back last 3 migrations
rails db:migrate:status # Show migration status
rails db:seed # Run db/seeds.rb
rails db:reset # Drop, create, migrate, seed
rails db:schema:load # Load schema.rb directly (faster than running all migrations)
# Rails console
rails console # Interactive Ruby with Rails loaded
rails console --sandbox # Rolls back all changes on exitKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free