Skip to content

Ruby on Rails - Active Records

Active Record is the M in MVC – the model layer. It’s the Object/Relational Mapping (ORM) system provided by Rails. Active Record allows you to interact with your database tables as if they were Ruby objects, abstracting away much of the SQL.

Key principles of Active Record:

  • Tables map to classes: A database table (e.g., books) corresponds to a Ruby class (e.g., Book).
  • Rows map to objects: Each row in a table represents an instance (object) of that class.
  • Columns map to object attributes: Columns in a table correspond to attributes of the objects.

Active Record provides methods for CRUD (Create, Read, Update, Delete) operations, as well as defining associations between models, validations, callbacks, and database queries.

When designing your database schema for a Rails application, follow these conventions:

  • Table Naming: Table names are plural and lowercase (e.g., books, authors). Model class names are singular and CamelCased (e.g., Book, Author).
  • Primary Keys: Each table should have an id column as its primary key (auto-incrementing integer by default). Active Record manages this automatically.
  • Foreign Keys: For associations (e.g., a book belongs to an author), the table with the belongs_to association (e.g., books) will have a foreign key column named after the associated model, suffixed with _id (e.g., author_id).
  • Timestamps: It’s conventional to have created_at and updated_at timestamp columns in your tables. Active Record can manage these automatically if they exist.

You can generate model files using the Rails generator command. For our library application, let’s create models for Book and Subject.

$ bin/rails generate model Book title:string price:decimal subject:references description:text
$ bin/rails generate model Subject name:string

This command does several things:

  • Creates model files: app/models/book.rb and app/models/subject.rb.
  • Creates migration files in db/migrate/ to define the schema for the books and subjects tables. The subject:references in the Book generator automatically adds a subject_id column and sets up a belongs_to association in the model if possible.
  • Creates test files (e.g., test/models/book_test.rb) and fixture files.

The generated app/models/book.rb might look like this (if subject:references was used effectively):

class Book < ApplicationRecord
belongs_to :subject # Added by 'subject:references'
end

And app/models/subject.rb:

class Subject < ApplicationRecord
end

Note: Models now inherit from ApplicationRecord, which itself inherits from ActiveRecord::Base. This provides a common place for application-wide model configurations.

Associations define relationships between models. Common association types include:

  • belongs_to: Indicates a one-to-one or one-to-many connection where this model has the foreign key (e.g., a Book belongs to a Subject).
  • has_many: Indicates a one-to-many relationship (e.g., a Subject has many Books).
  • has_one: Indicates a one-to-one relationship (e.g., a User has one Profile).
  • has_and_belongs_to_many (HABTM): For direct many-to-many relationships using a join table.
  • has_many :through: A more flexible way to set up many-to-many relationships, often preferred over HABTM as it allows attributes on the join model.

Let’s ensure our Book and Subject models have the correct associations. Assuming a book belongs to one subject, and a subject can have many books:

app/models/book.rb
class Book < ApplicationRecord
belongs_to :subject
# ... validations etc. ...
end
// app/models/subject.rb
class Subject < ApplicationRecord
has_many :books
# ... validations etc. ...
end

The subject:references in the Book model generator should have already added belongs_to :subject. We manually add has_many :books to Subject.

Validations ensure that only valid data is saved to your database. They are defined in the model.

Common validation helpers:

  • validates :attribute, presence: true (replaces validates_presence_of)
  • validates :attribute, uniqueness: true (replaces validates_uniqueness_of)
  • validates :attribute, numericality: true (replaces validates_numericality_of)
  • validates :attribute, length: { minimum: 5, maximum: 100 }
  • validates :attribute, format: { with: /regex/ }
  • validates :attribute, inclusion: { in: %w(small medium large) }
  • validates :attribute, exclusion: { in: %w(admin root) }

Let’s add some validations to app/models/book.rb:

app/models/book.rb
class Book < ApplicationRecord
belongs_to :subject
validates :title, presence: true, uniqueness: { scope: :subject_id, message: "should be unique for each subject" }
validates :price, numericality: { greater_than_or_equal_to: 0, message: "must be a positive number or zero" }, allow_nil: true
validates :description, length: { maximum: 500 }
end
// app/models/subject.rb
class Subject < ApplicationRecord
has_many :books
validates :name, presence: true, uniqueness: true
end

These validations will be checked automatically when you try to save a Book or Subject object (e.g., book.save). If validations fail, save returns false, and the errors can be accessed via book.errors.full_messages.

Active Record provides a rich query interface:

  • Book.all # Retrieves all books
  • Book.find(1) # Finds book with id 1
  • Book.find_by(title: 'Rails Guide') # Finds first book with this title
  • Book.where(subject_id: 2) # Finds all books belonging to subject with id 2
  • Book.where('price > ?', 50) # Books with price greater than 50
  • Book.order(created_at: :desc) # Orders books by creation date, descending
  • Book.limit(10) # Limits to 10 results
  • Book.joins(:subject).where(subjects: { name: 'Programming' }) # Joins and filters

These methods can be chained together to build complex queries.

After defining models, the next step is to create or update the database schema using Migrations. Migrations allow you to evolve your database structure over time in a consistent and version-controlled way.