100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Ruby

ActiveRecord Basics

An introduction to Rails' object-relational mapper, covering models, migrations, associations, validations, and the query interface used to interact with a database.

Gems, Testing & Rails BasicsIntermediate11 min readJul 9, 2026
Analogies

ActiveRecord Basics

ActiveRecord is the object-relational mapping (ORM) layer bundled with Rails. It implements the Active Record pattern, meaning each model class wraps a single database table and each instance of that class wraps a single row. Rather than writing raw SQL, developers call Ruby methods — User.find(1), user.save, user.destroy — and ActiveRecord translates them into the appropriate SQL for whichever database adapter is configured (PostgreSQL, MySQL, SQLite, and others). This abstraction dramatically speeds up development while still allowing raw SQL or Arel when needed for complex queries.

🏏

Cricket analogy: ActiveRecord is like a translator between a coach's spoken instructions and the scoreboard operator's button presses -- you call player.save in plain terms and it converts that into the exact scoreboard update, whether the ground uses a Duckworth-Lewis-capable system or a basic manual board.

Migrations and schema management

Migrations are versioned Ruby files that describe changes to the database schema — creating tables, adding columns, adding indexes — in a database-agnostic way. Running rails db:migrate applies pending migrations in order and records which have run in a schema_migrations table, so the schema can evolve safely across environments and be rolled back with rails db:rollback if needed. Migrations keep a team's database schema in version control alongside the application code, which is essential for reproducible deployments.

🏏

Cricket analogy: Migrations are like the official ICC ground-preparation logbook: each pitch change (relaying turf, adjusting boundary rope) is a dated, ordered entry so any curator anywhere can reproduce the exact same pitch conditions.

ruby
# db/migrate/20260701120000_create_articles.rb
class CreateArticles < ActiveRecord::Migration[7.1]
  def change
    create_table :articles do |t|
      t.string  :title, null: false
      t.text    :body
      t.references :author, null: false, foreign_key: { to_table: :users }
      t.boolean :published, default: false
      t.timestamps
    end
    add_index :articles, :title
  end
end

# app/models/article.rb
class Article < ApplicationRecord
  belongs_to :author, class_name: "User"
  has_many   :comments, dependent: :destroy

  validates :title, presence: true, length: { maximum: 200 }
  validates :body, presence: true

  scope :published, -> { where(published: true) }
end

# Usage
recent = Article.published.where("created_at > ?", 1.week.ago).order(created_at: :desc)
article = Article.create!(title: "Rails 7 tips", body: "...", author: current_user)

Associations

Associations like belongs_to, has_many, and has_and_belongs_to_many declare relationships between models and generate a rich set of methods automatically: article.author, author.articles, author.articles.build(title: "..."). The dependent: :destroy option ensures related records (comments, in the example above) are cleaned up when the parent is deleted, preventing orphaned rows. For many-to-many relationships, has_many :through is generally preferred over has_and_belongs_to_many because it allows the join model to carry its own attributes and validations.

🏏

Cricket analogy: has_many is like a captain's relationship to the squad: captain.players lists the team, player.captain points back, and dependent: :destroy is like disbanding the whole squad's contracts if the captaincy is vacated so no player is left in limbo.

Validations and callbacks

Validations run before a record is saved and prevent invalid data from reaching the database — validates :email, presence: true, uniqueness: true is a common pattern. Callbacks such as before_save, after_create, and after_commit hook into the object's lifecycle to perform side effects like sending a welcome email or normalizing data. Callbacks are powerful but should be used sparingly; overusing them scatters business logic across many small hooks and makes the flow of a single save call hard to trace.

🏏

Cricket analogy: Validations are like the umpire checking a bowler's run-up is legal before the ball is bowled -- validates :email, presence: true is the no-ball check that stops invalid deliveries, while callbacks like after_create are like the scoreboard automatically updating after a valid run is confirmed.

ActiveRecord's query interface is lazy: Article.where(published: true) does not hit the database until the relation is enumerated (via .each, .to_a, rendering in a view, etc). This lets you chain scopes like .where(...).order(...).limit(...) and have Rails combine them into a single efficient SQL query.

The N+1 query problem is one of the most common ActiveRecord performance bugs: looping over articles and calling article.author.name inside the loop issues one query per article. Use Article.includes(:author) to eager-load the association and collapse it into two queries total.

  • ActiveRecord maps each model class to a table and each instance to a row, translating Ruby method calls into SQL.
  • Migrations version-control schema changes and can be applied or rolled back with rails db:migrate / db:rollback.
  • Associations (belongs_to, has_many, has_many :through) generate convenience methods for related records.
  • Validations run before save and reject invalid data; callbacks hook into the object lifecycle for side effects.
  • ActiveRecord queries are lazy and chainable, letting scopes combine into a single SQL statement.
  • Eager loading with includes prevents the N+1 query problem when accessing associations in a loop.

Practice what you learned

Was this page helpful?

Topics covered

#Ruby#RubyStudyNotes#Programming#ActiveRecordBasics#ActiveRecord#Migrations#Schema#Management#StudyNotes#SkillVeris