Working with ActiveRecord Basics: Models, Queries, and Relationships
ActiveRecord is the ORM (Object-Relational Mapping) layer that comes with Ruby on Rails. Working with ActiveRecord basics means interacting with your database like you would with Ruby objects, with no need to write raw SQL for most operations. In this tutorial, you’ll learn how to create models, perform CRUD operations, define relationships, and build queries with ActiveRecord.
Before you begin
ActiveRecord basics are the part of Rails that connect your Ruby code to real application data. Once you know how models map to tables, the rest of the framework starts to feel more concrete because the objects in your code line up with the rows in your database. That makes it easier to reason about what happens when a record is loaded, changed, or saved.
This tutorial focuses on the everyday pieces you will use most often. The goal is not to memorize every method in the framework, but to understand the patterns that show up in normal Rails work so you can read the code and predict what it will do.
Key takeaways
- ActiveRecord turns tables into Ruby classes and rows into Ruby objects.
- CRUD, validations, scopes, and callbacks give you a lot of database power without writing raw SQL for every task.
- Associations keep related records connected in a readable way.
- Small query chains are easier to follow than large, one-off SQL strings.
What is ActiveRecord?
ActiveRecord is Rails’ implementation of the ORM pattern. It maps database tables to Ruby classes, and rows to objects. When you create a model in Rails, you get a powerful interface for interacting with your database.
# This automatically maps to a "users" table
class User < ApplicationRecord
end
Each attribute in the database becomes a method on your model:
Methods and classes are the building blocks of Ruby programs. The first example introduces a pattern, and the second shows a variation that handles a different use case. Learning to recognise these variations helps you read Ruby code more fluently and choose the right pattern for the problem you are solving. Each variation trades off simplicity, flexibility, and explicitness.
user = User.find(1)
user.name # => "Alice"
user.email # => "alice@example.com"
That object-oriented style is one of the reasons Rails feels so productive. Instead of moving back and forth between SQL and Ruby, you can usually stay in Ruby long enough to express the idea clearly. The database still matters, but ActiveRecord gives you a friendlier way to talk to it. Instead of moving back and forth between SQL and Ruby, you can usually stay in Ruby long enough to express the idea clearly. The database still matters, but ActiveRecord gives you a friendlier way to talk to it.
Creating Models
Generate a model with the Rails generator. This command creates both the model file and a database migration, which together form the Ruby-side and database-side of your new table:
rails generate model Article title:string body:text published:boolean
This creates:
app/models/article.rb— The model classdb/migrate/20260307123456_create_articles.rb— The migration
The model inherits from ApplicationRecord, which inherits from ActiveRecord::Base:
Methods and classes are the building blocks of Ruby programs. The first example introduces a pattern, and the second shows a variation that handles a different use case. Learning to recognise these variations helps you read Ruby code more fluently and choose the right pattern for the problem you are solving. Each variation trades off simplicity, flexibility, and explicitness.
# app/models/article.rb
class Article < ApplicationRecord
end
CRUD Operations
ActiveRecord provides methods for all standard database operations:
Create
Methods and classes are the building blocks of Ruby programs. The first example introduces a pattern, and the second shows a variation that handles a different use case. Learning to recognise these variations helps you read Ruby code more fluently and choose the right pattern for the problem you are solving. Each variation trades off simplicity, flexibility, and explicitness.
# Create a new article
article = Article.new(title: "Hello World", body: "My first post")
article.save
# Or use create to save in one step
article = Article.create(title: "Second Post", body: "Content here")
read
The create method combines new and save into a single call. After creation, ActiveRecord returns the persisted object with its database-assigned ID already set. Now retrieve records with the finder methods:
ActiveRecord queries are chainable by design. The first code block constructs part of a query, and the second extends or refines it. Because each method returns an ActiveRecord::Relation, you can build queries incrementally, adding filters, ordering, and limits as you go. This lazy evaluation means the database is only hit when you actually need the results.
# Find by primary key
article = Article.find(1)
# Find first match
article = Article.find_by(title: "Hello World")
# Get all records
articles = Article.all
# Find with conditions
published_articles = Article.where(published: true)
update
The where method returns an ActiveRecord::Relation, a lazy query object that only hits the database when you iterate or call a terminal method. Now update records by modifying attributes and calling save:
ActiveRecord queries are chainable by design. The first code block constructs part of a query, and the second extends or refines it. Because each method returns an ActiveRecord::Relation, you can build queries incrementally, adding filters, ordering, and limits as you go. This lazy evaluation means the database is only hit when you actually need the results.
article = Article.find(1)
article.title = "Updated Title"
article.save
# Or update in one step
article.update(title: "New Title", body: "New content")
# Update multiple records
Article.where(published: false).update_all(published: true)
delete
The update_all method skips validations and callbacks, running a single SQL UPDATE. Use it carefully when you need bulk changes and are confident the data is already clean. For single-record deletion, use destroy:
ActiveRecord queries are chainable by design. The first code block constructs part of a query, and the second extends or refines it. Because each method returns an ActiveRecord::Relation, you can build queries incrementally, adding filters, ordering, and limits as you go. This lazy evaluation means the database is only hit when you actually need the results.
article = Article.find(1)
article.destroy
# Delete without loading into memory
Article.destroy(1)
Article.destroy_all(published: true)
Validations
ActiveRecord lets you validate data before saving to the database:
Methods and classes are the building blocks of Ruby programs. The first example introduces a pattern, and the second shows a variation that handles a different use case. Learning to recognise these variations helps you read Ruby code more fluently and choose the right pattern for the problem you are solving. Each variation trades off simplicity, flexibility, and explicitness.
class Article < ApplicationRecord
validates :title, presence: true
validates :body, length: { minimum: 10 }
validates :slug, uniqueness: true
end
Common validations provide finer-grained control over what data is accepted. Each validation helper accepts options like presence, length, and format that map to clear error messages when the check fails:
Methods and classes are the building blocks of Ruby programs. The first example introduces a pattern, and the second shows a variation that handles a different use case. Learning to recognise these variations helps you read Ruby code more fluently and choose the right pattern for the problem you are solving. Each variation trades off simplicity, flexibility, and explicitness.
class User < ApplicationRecord
validates :email, presence: true
validates :email, format: { with: /\A[^@]+@[^@]+\z/ }
validates :age, numericality: { greater_than: 0 }
validates :username, length: { in: 3..20 }
validates :terms, acceptance: true
end
relationships
ActiveRecord makes defining database relationships simple through declarative macros. Each association type adds methods to your model automatically, so belongs_to :author gives you article.author and article.author= for free:
belongs to
The belongs_to association places the foreign key on the model that declares it. The other side of the relationship uses has_many, which expects the foreign key to live on the associated table. When you define both sides of the relationship, ActiveRecord automatically generates methods like author.articles and article.author that let you navigate between records in either direction.
class Article < ApplicationRecord
belongs_to :author
end
has many
The belongs_to association places the foreign key on the model that declares it. The other side of the relationship uses has_many, which expects the foreign key to live on the associated table:
has_many returns a collection proxy that supports chaining, iteration, and creation. The next example introduces has_one, which works like has_many but limits the result to a single record. This is useful for profiles, avatars, or settings where each parent record has exactly one associated child rather than a collection.
class Author < ApplicationRecord
has_many :articles
end
has one
has_one works like has_many but limits the result to a single record. It is commonly used for profiles, avatars, or settings where each parent record has exactly one associated child:
One-to-one and one-to-many associations cover most simple relationships, but real data models often need many-to-many links. The next example uses a join table, an intermediate model that holds foreign keys for both sides, to let articles have many tags and tags belong to many articles. The through option on has_many tells ActiveRecord to traverse the join table automatically.
class Profile < ApplicationRecord
belongs_to :user
has_one :avatar
end
many-to-many with join table
Methods and classes are the building blocks of Ruby programs. The first example introduces a pattern, and the second shows a variation that handles a different use case. Learning to recognise these variations helps you read Ruby code more fluently and choose the right pattern for the problem you are solving. Each variation trades off simplicity, flexibility, and explicitness.
class Article < ApplicationRecord
has_many :taggings
has_many :tags, through: :taggings
end
class Tagging < ApplicationRecord
belongs_to :article
belongs_to :tag
end
class Tag < ApplicationRecord
has_many :taggings
has_many :articles, through: :taggings
end
query interface
ActiveRecord provides a powerful chainable query interface. Each method returns an ActiveRecord::Relation, so you can stack where, order, and limit calls without hitting the database until you need the results:
Methods and classes are the building blocks of Ruby programs. The first example introduces a pattern, and the second shows a variation that handles a different use case. Learning to recognise these variations helps you read Ruby code more fluently and choose the right pattern for the problem you are solving. Each variation trades off simplicity, flexibility, and explicitness.
# Chaining scopes
articles = Article
.where(published: true)
.where("created_at > ?", 1.week.ago)
.order(created_at: :desc)
.limit(10)
Common query methods cover filtering, ordering, limiting, and aggregation. Each method builds onto the relation without executing SQL immediately, so you can compose queries incrementally:
ActiveRecord models grow through composition of small declarative macros. The first block shows one aspect of model behaviour, and the second adds another layer like validations, associations, or scopes. Understanding how these pieces interact is key to building Rails applications that stay maintainable as the data model grows more complex.
# Filtering
Article.where(published: true) # WHERE clause
Article.order(:title) # ORDER BY
Article.limit(5) # LIMIT
Article.offset(10) # OFFSET
Article.select(:title, :body) # SELECT specific columns
Article.distinct # SELECT DISTINCT
# Aggregation
Article.count # COUNT
Article.sum(:view_count) # SUM
Article.average(:rating) # AVG
Article.minimum(:price) # MIN
Article.maximum(:price) # MAX
scopes
Scopes are reusable query chains defined in your model. They keep common query patterns in one place so controllers and other models can use them without repeating the same where clauses:
Methods and classes are the building blocks of Ruby programs. The first example introduces a pattern, and the second shows a variation that handles a different use case. Learning to recognise these variations helps you read Ruby code more fluently and choose the right pattern for the problem you are solving. Each variation trades off simplicity, flexibility, and explicitness.
class Article < ApplicationRecord
# Simple scope
scope :published, -> { where(published: true) }
# Scope with arguments
scope :recent, ->(days) { where("created_at > ?", days.days.ago) }
# You can also define as class methods
def self.popular
where("view_count > ?", 1000)
end
end
# Usage
Article.published.recent(7)
Article.popular.where(title: "Hello")
callbacks
Callbacks let you run code at specific points in the object lifecycle. They are useful for tasks like generating slugs, setting defaults, or logging changes, but they should stay lightweight because they fire automatically on save and destroy:
Methods and classes are the building blocks of Ruby programs. The first example introduces a pattern, and the second shows a variation that handles a different use case. Learning to recognise these variations helps you read Ruby code more fluently and choose the right pattern for the problem you are solving. Each variation trades off simplicity, flexibility, and explicitness.
class Article < ApplicationRecord
before_validation :set_slug
private
def set_slug
self.slug = title.parameterize if slug.blank?
end
end
Common callbacks:
before_validation/after_validationbefore_save/around_save/after_savebefore_create/around_create/after_createbefore_update/around_update/after_updatebefore_destroy/around_destroy/after_destroy
Migrations
Migrations manage your database schema over time:
Methods and classes are the building blocks of Ruby programs. The first example introduces a pattern, and the second shows a variation that handles a different use case. Learning to recognise these variations helps you read Ruby code more fluently and choose the right pattern for the problem you are solving. Each variation trades off simplicity, flexibility, and explicitness.
# Create a migration
rails generate migration AddPublishedAtToArticles published_at:datetime
# Run migrations
rails db:migrate
# Rollback
rails db:rollback
Common migration operations include creating tables, adding and removing columns, and setting defaults. Each operation maps to a SQL statement that Rails generates based on your database adapter:
Methods and classes are the building blocks of Ruby programs. The first example introduces a pattern, and the second shows a variation that handles a different use case. Learning to recognise these variations helps you read Ruby code more fluently and choose the right pattern for the problem you are solving. Each variation trades off simplicity, flexibility, and explicitness.
class CreateArticles < ActiveRecord::Migration[7.1]
def change
create_table :articles do |t|
t.string :title
t.text :body
t.boolean :published, default: false
t.timestamps
end
end
end
Migrations are part of the long-term history of your application, so they should read like a series of careful changes rather than a pile of one-off tweaks. Good migrations are explicit, reversible when possible, and small enough that another developer can understand what changed without digging through the whole schema. That habit pays off the first time you need to review or roll back a change.
example: complete article model, so they should read like a series of careful changes rather than a pile of one-off tweaks. Good migrations are explicit, reversible when possible, and small enough that another developer can understand what changed without digging through the whole schema. That habit pays off the first time you need to review or roll back a change.
example: complete article model
Here’s a more complete example bringing together what you’ve learned:
class Article < ApplicationRecord
# Relationships
belongs_to :author
has_many :comments, dependent: :destroy
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings
# Validations
validates :title, presence: true, length: { minimum: 5 }
validates :body, presence: true
validates :slug, uniqueness: true
# Scopes
scope :published, -> { where(published: true) }
scope :drafts, -> { where(published: false) }
scope :recent, -> { order(created_at: :desc) }
# Callbacks
before_validation :generate_slug
private
def generate_slug
self.slug = title.parameterize if slug.blank?
end
end
common pitfalls
Watch out for these common issues. The model above brings together relationships, validations, scopes, and callbacks in a realistic pattern you will see in production Rails applications. Now be aware of these frequent mistakes:
Eager loading with includes loads associated records in a single query instead of one query per record. Another common pitfall is bypassing validations when updating columns. The next example shows the difference between update_column, which skips validations and callbacks, and update, which runs them both. Prefer update unless you have a specific performance reason to skip the model lifecycle.
# BAD: N+1 query problem
articles = Article.all
articles.each do |article|
puts article.author.name # Each access triggers a query!
end
# GOOD: Eager loading
articles = Article.includes(:author).all
articles.each do |article|
puts article.author.name # Authors loaded in one query
end
Eager loading with includes loads associated records in a single query instead of one query per record. Another common pitfall is bypassing validations when updating columns:
The distinction between update_column and update matters because validations protect your data integrity. Bypassing them with update_column can leave a record in an invalid state with no error message to explain why. The following FAQ section answers common questions about when to use ActiveRecord features and how to avoid the most frequent mistakes developers make with models, queries, and associations.
# BAD: Using update_column (bypasses validations)
article.update_column(:published, true)
# GOOD: Use update (runs validations)
article.update(published: true)
Frequently asked questions
Should I always use ActiveRecord instead of SQL?
No. ActiveRecord is great for most everyday CRUD work, but raw SQL still has a place when you need a query that is easier to express directly or when performance tuning calls for a specific database feature. The point is to choose the clearest tool for the job.
Are callbacks a good place for business logic?
Sometimes, but not usually for complex workflows. Callbacks are best for small, predictable changes that always belong to the model lifecycle, such as setting a slug or updating a timestamp. If the logic reaches out to other systems or starts to branch a lot, a service object is often easier to understand.
When should I stop chaining query methods?
When the chain stops reading like a sentence. A few filters, an order, and a limit are usually fine. When the chain starts to hide the important decision, it is often better to extract a scope or a named query method so the intention stays visible.
Common mistakes
- Loading too many records into memory when a narrower query would do the job.
- Hiding expensive database work inside a callback where it is easy to miss.
- Using
update_columnor similar shortcuts when validations and callbacks should still run. - Adding too many responsibilities to a single model instead of splitting the workflow into a smaller object.
Summary
ActiveRecord is the heart of data handling in Rails:
- Models map to database tables
- CRUD operations are simple method calls
- Validations ensure data integrity
- Relationships connect models elegantly
- Query Interface builds SQL chains in Ruby
- Scopes create reusable queries
- Callbacks hook into the object lifecycle
Master ActiveRecord and you’ll be able to build data-driven Rails applications efficiently without touching raw SQL.
Next steps
Once you understand models and queries, the next useful Rails topics are request flow and presentation. If you want to keep going, read Rails routing to see how requests reach controllers, and then Rails views and partials to see how the data is rendered back to the browser.
The next step is usually learning how these models interact with controllers and views, because that is where the data starts to move through the rest of the application. Once you understand the model layer well, the rest of Rails becomes much easier to follow.
In the next tutorial, we’ll explore Rails routing and see how HTTP requests are mapped to controller actions.