Active Record basics

Ruby on Rails · Essentials · 14 min

Active Record is how Rails apps talk to the database, and its query interface is used in nearly every file.

A model is a class inheriting from ApplicationRecord. Active Record reads the table's columns and creates attribute methods automatically, so class Article < ApplicationRecord; end is a complete model.

Create: Article.new(title: "Hi") then save, or Article.create(...). Read: find(id) (raises RecordNotFound), find_by(slug: "hi") (returns nil), first, last, all. Update: article.update(title: "New"). Delete: article.destroy (runs callbacks) or delete_all/destroy_all on a relation.

Bang methods (save!, create!, update!) raise on validation failure; the plain versions return false. Use bang versions where failure would be a bug, such as in scripts and jobs.

Queries chain into a relation, which is lazy: no SQL runs until you iterate, call to_a, first, count or similar.

Article.where(published: true).where("created_at > ?", 1.week.ago).order(created_at: :desc).limit(10)
  • Hash conditions (where(status: "draft"), where(id: [1, 2]), where(created_at: 1.day.ago..)) and ? placeholders are safe. Never interpolate input into SQL strings.
  • where.not(...), or(...), select, distinct, group, having.
  • pluck(:title) returns plain values without building model objects; exists? and count run cheap queries.
  • find_each iterates in batches of 1000 for big tables.

.to_sql shows the SQL a relation will run, and the console logs every query.

Example

article = Article.create!(title: "Hello Rails", body: "First", published: true)
article.update!(title: "Hello again")
p article.persisted?                     # true

recent = Article.where(published: true)
                .where(created_at: 1.week.ago..)
                .order(created_at: :desc)
                .limit(5)                # nothing queried yet
puts recent.to_sql
# SELECT "articles".* FROM "articles" WHERE "articles"."published" = 1
#   AND "articles"."created_at" >= '...' ORDER BY ... LIMIT 5
p recent.map(&:title)                    # query runs here

p Article.find_by(title: "nope")         # nil
p Article.where(published: false).count  # SELECT COUNT(*) ...
p Article.pluck(:id, :title).first       # [1, "Hello again"]

term = "Rails"
Article.where("title LIKE ?", "%#{term}%")   # safe: bound parameter
Article.find_each(batch_size: 500) { |a| a.touch }

Remember

  • Models map to tables; attributes come from columns automatically
  • Relations are lazy and chainable; SQL runs when results are needed
  • Use hash conditions or ? placeholders, never string interpolation

Watch out for

  • Writing where("title = '#{params[:q]}'"), which is a SQL injection vulnerability.

Check yourself

  1. When does Article.where(published: true).order(:title) hit the database?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: guides.rubyonrails.org/active_record_querying.html