Skip to main content Link Search Menu Expand Document (external link)

Active Record Basics

On this page, we will learn new a feature from Ruby on Rails. The Active Record which is responsible for the relationship between the model and the database.

Table of contents

  1. What is Active Record?
  2. What is Migration?
  3. Naming Conventions
  4. Active Record Basics
    1. CREATING NEW RECORDS
      1. create
      2. save
    2. READING RECORDS FROM A DATABASE
      1. all
      2. first
      3. find
      4. find_by
      5. where
      6. order
    3. UPDATE EXISTING RECORDS
      1. update
      2. save
      3. updating multiple records
    4. DELETING UNWANTED RECORDS
      1. destroy
      2. deleting multiple records

What is Active Record?

Ruby on Rails is based on MVC (model-view-controller) framework, we will discuss this on the later topics.

The Active Record represents the M in the MVC which is the model. Active Record represents business data and logic and also responsible dealings with the database on how to manage your data.

On this page we will look at how Active Record works.

What is Migration?

Migration is like version control for databases. We use this to monitor our current database schema. Writing a migration allows us to describe and transform the database schema according to our needs.

In the next paragraphs we will generate a migration file. You need to know how to do it because migration files are the common files we generate here in Ruby on Rails.

Naming Conventions

Ruby on Rails has a lot of magic, by following the correct naming conventions saves us from writing more code.

Let’s look at how Rails naming conventions for models and tables. Active Record uses naming conventions to map the relationships of models and database tables.

Ruby on Rails maps the models to tables by pluralizing the class names. To find which table it should use.

It makes sense when you think about databases that hold all the records of your models and makes pluralized work.

Models use singular since it’s only defined or describes a single row in a database.

For example:
We have a model Post since rails pluralize the class names of models and then look for a table that corresponds with it, which is the posts table.

# posts -> Rails recommends using pluralize nouns for table names.
# app/models/post.rb -> Ruby recommends using `snake_case` for file names
# Post -> # Ruby recommends using `PascalCase` for class names.
class Post < ApplicationRecord 
end

Reminder:

If your model’s corresponding database table does not fit this convention, you may manually specify the model’s table name by using the table_name on the model.

class Post < ApplicationRecord
  self.table_name = 'blog_lists'
end

Active Record Basics

In this part, we will practice some basic features of Active Record.

  • Open up your terminal and run docker-compose up -d to serve your project containers.

Now that your containers are up and running.

  • Let’s open a terminal in your container.
  • Run: docker-compose exec app bash
  • Let’s generate a migration file.
  • Run: rails generate migration create_posts
 $~/KodaCamp> docker-compose up -d
Creating kodacamp_project_redis_1 ... done
Creating kodacamp_project_db_1    ... done
Creating kodacamp_project_app_1   ... done
 $~/KodaCamp> docker-compose exec app bash
 root@0122:/usr/src/app# rails generate migration create_posts
       invoke  active_record
       create  db/migrate/xxxxxxxxxxxxxx_create_posts.rb

All generated migration files are stored in db/migrate directory.

We use create_posts as the name for our migration file and Ruby on Rails is smart enough to know that we want to create posts table.

The migration files are prefixed with numbers. These are the timestamps offered by Ruby on Rails to help you organize your migration files. It avoids naming conflicts, manages the history of your tables, and makes monitoring new migrations simple.

class CreatePosts < ActiveRecord::Migration[7.0]
  def change
    create_table :posts do |t|

      t.timestamps
    end
  end
end

When we generate a migration file, the t.timestamps field is already there. This line adds two new columns, created_at and updated_at, Active Record will handle the contents of these fields.

  • Let’s add a new column called title. This column only takes a small number of characters. We will use string as our data type.

  • Let’s also add one more column called content. We know that content could contain a lot of paragraphs, which means we need a data type that can hold it like text.

You can read more about migration data types in this link.

# db/migrate/xxxxxxxxxxxxxx_create_posts.rb

class CreatePosts < ActiveRecord::Migration[7.0]
  def change
    create_table :posts do |t|
+     t.string :title
+     t.text :content
      t.timestamps
    end
  end
end 

Read carefully:

rails db:migrate

This will run all the migration files that are not migrated yet. After you run this command do not modify your last migration file.

Reminder:

If you want to do some changes from your last migration you can run rails db:rollback but when the migration file you want to change is already behind, please generate a new one for that.

  • Now let’s run rails db:migrate
 root@0122:/usr/src/app# rails db:migrate
   == xxxxxxxxxxxxxx CreatePosts: migrating ======================================
   -- create_table(:posts)
   -> 0.0130s
   == xxxxxxxxxxxxxx CreatePosts: migrated (0.0133s) =============================

You see it generates a new table called posts and a new file db/schema.rb is added in your project.

Read carefully :

Schema only updates when you run a migration command, it only shows the current version of your database. Do not write or update anything to your schema manually because it does not reflect on your database. Generate a new migration file when you want to add or remove columns to your database.

ActiveRecord::Schema[7.0].define(version: xxxx_xx_xx_xxxxxx) do
  create_table "posts", force: :cascade do |t|
    t.string "title"
    t.text "content"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end

Now that we have a posts table. Next, we need to add a model for our table.

  • In the app/models, create a new file post.rb
# app/models/post.rb

class Post < ApplicationRecord
end

With these few lines of code, we can now use the Active Record.

  • Let’s back to your terminal and run rails c or rails console

Reminder:

The console command lets you interact with your Rails application from the command line. You can use exit to close the irb console.

 root@0122:/usr/src/app# rails c
   Loading development environment (Rails 7.0.4)
   irb(main):001:0>

CREATING NEW RECORDS

Read the following methods and try to understand how to use them.

create

This method accepts keyword arguments for your columns and their values.

irb(main):001:0> Post.create(title: 'My first post', content: 'My first post blog')
  TRANSACTION (0.7ms)  BEGIN
  Post Create (1.6ms)  INSERT INTO `posts` (`title`, `content`, `created_at`, `updated_at`) VALUES ('My first post', 'My first post blog', 'xx-xx-xx xx:xx:xx.xxxxxx', 'xx-xx-xx xx:xx:xx.xxxxxx')
  TRANSACTION (3.1ms)  COMMIT                                                                           
=> #<Post:0x000055ec95bd8698 id: 2, title: "My first post", content: "My first post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>

save

Using this method, we instantiate a record first before sending it to the database.

post = Post.new                         # Instantiate a model, This is like we create an empty row.
post.title = 'My second post'           # Add the values of these columns.
post.content = 'My second post blog'    # At this point, the record is still not saved in the database. 
post.save                               # After we call the `save` method, that is the time when the record saves in a database.
irb(main):001:0> post = Post.new
=> #<Post:0x000055ec95fc22e0 id: nil, title: nil, content: nil, created_at: nil, updated_at: nil>
irb(main):002:0> post.title = 'My second post'
=> "My second post"
irb(main):003:0> post.content = 'My second post blog'
=> "My second post blog"
irb(main):004:0> post.save
  TRANSACTION (0.5ms)  BEGIN
  Post Create (0.6ms)  INSERT INTO `posts` (`title`, `content`, `created_at`, `updated_at`) VALUES ('My second post', 'My second post blog', 'xx-xx-xx xx:xx:xx.xxxxxx', 'xx-xx-xx xx:xx:xx.xxxxxx')
  TRANSACTION (1.8ms)  COMMIT                                             
=> true                                                                   
  • Let’s add more records before we proceed.
Post.create(title: 'My third post', content: 'My third post blog')
Post.create(title: 'My fourth post', content: 'My fourth post blog')
Post.create(title: 'My fifth post', content: 'My fifth post blog')
Post.create(title: 'My sixth post', content: 'My sixth post blog')

READING RECORDS FROM A DATABASE

We have a lot of methods for fetching records from the database. Read the following methods and try to understand how to use them.

all

This method fetches all the records from the database and returns an array of model instance.

irb(main):001:0> Post.all
  Post Load (0.7ms)  SELECT `posts`.* FROM `posts`
=>                                                     
[#<Post:0x000055ec95bea000 id: 1, title: "My first post", content: "My first post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x000055ec95be9f38 id: 2, title: "My second post", content: "My second post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x000055ec95be9e70 id: 3, title: "My third post", content: "My third post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x000055ec95be9da8 id: 4, title: "My fourth post", content: "My fourth post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x000055ec95be9ce0 id: 5, title: "My fifth post", content: "My fifth post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x000055ec95be9c18 id: 6, title: "My sixth post", content: "My sixth post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>]

first

This method fetches the first record from the database and only returns a single instance of the model object.

irb(main):001:0> Post.first
Post Load (0.8ms)  SELECT `posts`.* FROM `posts` ORDER BY `posts`.`id` ASC LIMIT 1
=> #<Post:0x000055ec9553ff70 id: 1, title: "my first post", content: "My first post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>

Reminder:

In Default, Ruby on Rails use ascending order of primary id when fetching the records.

find

This method accepts an integer as an argument and returns the first record with matching id.

irb(main):001:0> Post.find(1)
  Post Load (0.5ms)  SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
=> #<Post:0x00007f4d0c054870 id: 1, title: "my first post", content: "My first post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>

find_by

This method accepts keyword arguments of columns and their values and returns the first matching record from your arguments.

irb(main):001:0> Post.find_by(title: 'my first post') 
  Post Load (0.6ms)  SELECT `posts`.* FROM `posts` WHERE `posts`.`title` = 'my first post' LIMIT 1
=> #<Post:0x00005564bacfdc20 id: 1, title: "my first post", content: "My first post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>

where

This method accepts keyword arguments with columns and their values as arguments and returns a collection of matching records from your arguments.

irb(main):001:0> Post.where(title: 'My sixth post')
Post Load (0.6ms)  SELECT `posts`.* FROM `posts` WHERE `posts`.`title` = 'My sixth post'
=> [#<Post:0x00007f3634458fc8 id: 6, title: "My sixth post", content: "My sixth post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>]
irb(main):004:0>

In this textbook, we will often encounter these methods. We’ve previously seen that the find and find_by methods return a single instance, while the where function returns an array of instances.

What happens if their records do not match any other records?.

  • The find method raises an ActiveRecord::RecordNotFound exception.

  • The find_by method returns a nil value.

  • The where method returns an empty array.

Think about where or when to use these methods because they look similar but not really

order

This method accepts keyword arguments with columns and values of :asc or :desc returns a collection in the order you set.

Post.order(created_at: :desc)

We can use other columns according to your needs and change the direction with the use of :asc for ascending and :desc for descending.

irb(main):001:0> Post.order(created_at: :desc)
  Post Load (0.7ms)  SELECT `posts`.* FROM `posts` ORDER BY `posts`.`created_at` DESC
=>                                                              
[#<Post:0x00005564bbf5e568 id: 6, title: "My sixth post", content: "My sixth post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x00005564bbf5e4a0 id: 5, title: "My fifth post", content: "My fifth post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x00005564bbf5e3d8 id: 4, title: "My fourth post", content: "My fourth post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x00005564bbf5e310 id: 3, title: "My third post", content: "My third post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x00005564bbf5e248 id: 2, title: "My second post", content: "My second post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x00005564bbf5e180 id: 1, title: "My first post", content: "My first post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>]

Reminder:

You need to know the difference between the methods that returns an array of model objects and single model object.

Methods that return an array of model instance:

  • all
  • where
  • order
  • limit
  • take

Methods that return a single model instance:

  • find
  • find_by
  • first
  • last

UPDATE EXISTING RECORDS

We need to find the record or records that we want to change.

update

  • This method only needs the keyword arguments for your columns and their values.
irb(main):001:0> post = Post.first
irb(main):002:0> post.update(content: 'The first post is updated') 
  Post Load (1.8ms)  SELECT `posts`.* FROM `posts` ORDER BY `posts`.`id` ASC LIMIT 1
  TRANSACTION (0.3ms)  BEGIN                                                                                 
  Post Update (0.6ms)  UPDATE `posts` SET `posts`.`content` = 'The first post is updated', `posts`.`updated_at` = 'xx-xx-xx xx:xx:xx.xxxxxx' WHERE `posts`.`id` = 1
  TRANSACTION (1.4ms)  COMMIT
=> true

save

  • This method is also applicable for updating a record. When the instantiated model is in the database, the record is updated instead of adding a new one.
irb(main):001:0> post = Post.second
  Post Load (1.2ms)  SELECT `posts`.* FROM `posts` ORDER BY `posts`.`id` ASC LIMIT 1 OFFSET 1
=> #<Post:0x00005564bc2bce08 id: 2, title: "test", content: "My first post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>
irb(main):002:0> post.content = 'The second post is updated'
irb(main):003:0> post.save
  TRANSACTION (0.4ms)  BEGIN
  Post Update (0.5ms)  UPDATE `posts` SET `posts`.`content` = 'The second post is updated', `posts`.`updated_at` = 'xx-xx-xx xx:xx:xx.xxxxxx' WHERE `posts`.`id` = 2
  TRANSACTION (1.0ms)  COMMIT                     
=> true

updating multiple records

  • From the first two examples we only update single records. Now we will try to update multiple records.

  • The update method can also be used to an array of records.

irb(main):001:0> Post.where(id: [3, 4]).update(content: 'Update multiple contents')
  Post Load (0.4ms)  SELECT `posts`.* FROM `posts` WHERE `posts`.`id` IN (1, 2)
  TRANSACTION (0.2ms)  BEGIN                                                                     
  Post Update (2.0ms)  UPDATE `posts` SET `posts`.`content` = 'Update multiple contents', `posts`.`updated_at` = 'xx-xx-xx xx:xx:xx.xxxxxx' WHERE `posts`.`id` = 3
  TRANSACTION (1.1ms)  COMMIT                                                                    
  TRANSACTION (0.3ms)  BEGIN                                                                     
  Post Update (0.3ms)  UPDATE `posts` SET `posts`.`content` = 'Update multiple contents', `posts`.`updated_at` = 'xx-xx-xx xx:xx:xx.xxxxxx' WHERE `posts`.`id` = 4
  TRANSACTION (1.4ms)  COMMIT                                                                    
=>                                                                                               
[#<Post:0x000055960d6d20d8 id: 3, title: "My third post", content: "Update multiple contents", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x000055960d6d1f20 id: 4, title: "My fourth post", content: "Update multiple contents", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>]
  • You can also use update_all method for updating an array of records.
irb(main):001:0> Post.where(id: [3, 4]).update_all(content: 'Update multiple contents with update_all')
Post Update All (0.6ms)  UPDATE `posts` SET `posts`.`content` = 'Update multiple contents with update_all' WHERE `posts`.`id` IN (3, 4)
=> 2                                                                   
  • This method returns the number of updated rows.

When we are using update method to update multiple records, Ruby on Rails instantiate the records and sends it to the database. The update_all use the SQL update statement and sends the update one time.

DELETING UNWANTED RECORDS

We need to find the record or records that we want to remove.

destroy

After you instantiate the model, you can remove a specific record using this method.

irb(main):001:0> post = Post.last
Post Load (0.8ms)  SELECT `posts`.* FROM `posts` ORDER BY `posts`.`id` DESC LIMIT 1
=> #<Post:0x00007f363463db40 id: 6, title: "My sixth post", content: "My sixth post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>
irb(main):002:0> post.destroy
TRANSACTION (0.4ms)  BEGIN
Post Destroy (0.5ms)  DELETE FROM `posts` WHERE `posts`.`id` = 6
TRANSACTION (1.5ms)  COMMIT                                   
=> #<Post:0x00007f363463db40 id: 6, title: "My sixth post", content: "My sixth post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>

deleting multiple records

The destroy method does not work on an array of instance, unlike the update.

We will be using the destroy_all method to remove an array of instances from our database.

irb(main):001:0> posts = Post.where(id: [3, 4])
irb(main):002:0> posts.destroy_all
  TRANSACTION (0.4ms)  BEGIN
  Post Destroy (0.5ms)  DELETE FROM `posts` WHERE `posts`.`id` = 3
  TRANSACTION (1.2ms)  COMMIT                            
  TRANSACTION (0.4ms)  BEGIN                             
  Post Destroy (0.4ms)  DELETE FROM `posts` WHERE `posts`.`id` = 4
  TRANSACTION (1.4ms)  COMMIT                            
=>                                                       
[#<Post:0x00007f36345e8168 id: 3, title: "My third post", content: "My third post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>,
 #<Post:0x00007f36345e80a0 id: 4, title: "My fourth post", content: "My fourth post blog", created_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx, updated_at: xxx, xx xxx xxxx xx:xx:xx.xxxxxxxxx UTC +xx:xx>]

Read carefully:

The destroy_all and update_all method does not run your validations or callbacks.

That’s all for the Active Record basics.


Back to top

Copyright © 2020-2022 Secure Smarter Service, Inc. This site is powered by KodaCamp.