Altcademy - a Forbes magazine logo Best Coding Bootcamp 2023

What is Object-Relational Mapping (ORM) in Ruby on Rails?

Object-Relational Mapping, or ORM, is a technique used in modern software development to abstract the interaction between a programming language and a relational database management system (RDBMS). Ruby on Rails (RoR) is a popular web application framework that makes use of ORM through ActiveRecord, its default ORM library. In this blog post, we will explore what ORM is, how it works in Ruby on Rails, and some examples to illustrate the concepts.

Why Object-Relational Mapping?

When developing software applications, we often need to persist data, or store information for future use. One common solution for persisting data is using relational databases. These databases store data in tables with rows and columns, allowing us to organize and manipulate the data efficiently.

However, interacting with relational databases directly can be cumbersome, as we have to write SQL (Structured Query Language) queries to insert, update, delete, and retrieve data. This is where ORM comes in. ORM helps us interact with the database using the programming language's objects, making the process much more natural and intuitive.

To put it simply, ORM allows us to treat the rows in a database table as objects in our programming language. This means that we can interact with the database in the same way we would with any other object in our code, without having to deal with SQL syntax.

Understanding ActiveRecord

ActiveRecord is the default ORM library used in Ruby on Rails. It helps us interact with the database using Ruby objects, without having to write SQL queries. ActiveRecord follows the "convention over configuration" principle, meaning it provides sensible defaults that work out of the box, allowing us to focus on writing code rather than configuring settings.

ActiveRecord uses Ruby classes called "models" to represent the tables in the database. Each model class inherits from ActiveRecord::Base, which provides the methods and functionality needed to interact with the database.

For example, let's say we have a table in our database called "products" with columns "id", "name", and "price". We would create a Ruby class called "Product" that inherits from ActiveRecord::Base like this:

class Product < ActiveRecord::Base
end

Now, we can use this "Product" class to interact with the "products" table in the database.

Creating a Rails Application with ActiveRecord

To demonstrate how ActiveRecord works, let's create a simple Rails application called "Shop". First, we need to install Rails if we haven't already. Run the following command in your terminal:

gem install rails

Next, let's create a new Rails application called "Shop":

rails new Shop

This will create a new directory called "Shop" with the necessary files and directories for a Rails application. Now, let's navigate to the "Shop" directory and create a model called "Product":

cd Shop
rails generate model Product name:string price:decimal

This command generates a new "Product" model with "name" and "price" attributes, as well as a migration file to create the "products" table in the database. Let's run the migration using:

rails db:migrate

Now, we have a "products" table in our database and a "Product" model to interact with it using ActiveRecord.

Performing CRUD Operations

CRUD stands for Create, Read, Update, and Delete - the four basic operations we perform on data. ActiveRecord provides methods for performing these operations on our models.

Create

To create a new product, we can use the create method:

product = Product.create(name: 'Laptop', price: 1000)

This will create a new row in the "products" table with the specified "name" and "price" attributes.

Read

To retrieve data from the database, we can use various methods provided by ActiveRecord:

  • all: Returns all records of the model.
products = Product.all
  • find: Returns a single record with the specified ID.
product = Product.find(1)
  • where: Returns all records that match the specified conditions.
expensive_products = Product.where('price > ?', 500)

Update

To update an existing record, we can use the update method:

product = Product.find(1)
product.update(name: 'New Laptop', price: 1200)

This will update the record with ID 1 in the "products" table with the new "name" and "price" attributes.

Delete

To delete a record, we can use the destroy method:

product = Product.find(1)
product.destroy

This will delete the record with ID 1 from the "products" table.

Associations

In a relational database, we often need to create relationships between tables. ActiveRecord makes it easy to define these relationships using associations. There are four types of associations in ActiveRecord:

  • belongs_to
  • has_one
  • has_many
  • has_and_belongs_to_many

Let's say we have another table called "categories" with columns "id" and "name". We want to associate each product with a category. First, we need to create a new model called "Category" and a migration to create the "categories" table:

rails generate model Category name:string
rails db:migrate

Now, we need to modify the "products" table to include a foreign key called "category_id". We do this by creating a new migration:

rails generate migration AddCategoryIdToProducts category_id:integer
rails db:migrate

Next, we need to define the association between the "Product" and "Category" models. In the "Product" model, add the following line:

belongs_to :category

And in the "Category" model, add this line:

has_many :products

Now, we can easily associate products with categories using ActiveRecord:

category = Category.create(name: 'Electronics')
product = Product.create(name: 'Laptop', price: 1000, category: category)

We can also retrieve the associated objects using the association methods:

product.category # Returns the associated category
category.products # Returns all products associated with the category

Validations

Validations are an important part of any application, ensuring that the data we store is correct and consistent. ActiveRecord provides a simple way to define validations for our models.

For example, let's say we want to ensure that each product has a unique name and a price greater than zero. In the "Product" model, we can add the following validations:

validates :name, uniqueness: true
validates :price, numericality: { greater_than: 0 }

Now, if we try to create or update a product with an invalid name or price, ActiveRecord will prevent the operation and add an error message to the model:

product = Product.create(name: 'Laptop', price: -100)
product.errors.full_messages # Returns ["Price must be greater than 0"]

Migrations

Migrations are a way to evolve our database schema over time while preserving existing data. They allow us to make changes to the database, such as creating or modifying tables and columns, in a structured and version-controlled way.

We have already seen examples of migrations when creating the "products" and "categories" tables and adding the "category_id" column to the "products" table. Migrations are defined as Ruby classes that inherit from ActiveRecord::Migration and include methods to perform the desired changes.

For example, here is a migration to create the "products" table:

class CreateProducts < ActiveRecord::Migration
  def change
    create_table :products do |t|
      t.string :name
      t.decimal :price

      t.timestamps
    end
  end
end

When we run the rails db:migrate command, Rails will execute the change method, which creates the "products" table with the specified columns.

If we need to reverse a migration, we can use the rails db:rollback command. This will call the down method of the migration, which undoes the changes made by the up method.

Conclusion

In this blog post, we have explored the concept of Object-Relational Mapping and its implementation in Ruby on Rails through ActiveRecord. We have seen how ActiveRecord allows us to interact with the database using Ruby objects, making it easier and more intuitive to perform CRUD operations, define associations, add validations, and manage migrations.

By understanding and utilizing ActiveRecord in your Rails applications, you can create more efficient and maintainable code while improving the overall quality of your projects.