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?andcountrun cheap queries.find_eachiterates 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.