October 3, 2023

Viiisit [Ruby on Rails] - Active Record!

#ruby on rails#active record

今天來點 Active Record!

在講述 Active Record 之前,我們先來建立一個基礎概念:ORM Object–relational mapping

Object–relational mapping (ORM) 物件關聯映射

物件關聯映射(ORM)是一種軟體設計模式,將資料庫中的數據映射到物件導向程式語言中的物件,使開發人員能夠使用物件導向的方式處理數據,而不必直接處理SQL查詢。
用一個圖書館管理系統的例子來說明不使用 ORM 和使用 ORM 之間的差異。

不使用 ORM 的圖書館管理系統:

在不使用 ORM 的情況下,需要手動建立 Table,並編寫 SQL 查詢將資料存到資料庫中或從資料庫中檢索資料。

在這種情況下,需要自己處理資料庫結構的細節和 SQL 查詢的編寫。

使用 ORM 的圖書館管理系統:

使用ORM,可以更輕鬆地處理資料,並將其映射到物件。

使用 ORM,可以將資料庫表格映射到模型 (Model),並使用 Model 方法來執行資料庫操作,而不必編寫原生 SQL 查詢或擔心資料庫結構的詳細資訊,使得代碼更容易閱讀、維護和擴展。


在理解完 ORM 之後,接著來看看 Active Record 吧!

Active Record

Active Record 是一個具體的 ORM 實現。他提供了一種方式來定義和操作 Model,隱藏了資料庫操作的細節,允許開發人員使用物件導向語法來處理資料。Active Record 還提供了方法來執行數據庫查詢、新增、更新和刪除記錄,並建立了 Model 和資料表之間的映射關係,而 Model 在 Rails 中處理與資料庫的互動、商業邏輯、驗證。

接續 Active Record 的概念,讓我們來了解如何建立一個新的 Model(模型)並與資料庫關聯。


CRUD 新增、讀取、更新與刪除

當使用 Active Record 在 Ruby on Rails 中進行 CRUD(新增、讀取、更新和刪除)操作時,通常使用以下方法:

  1. 新增(Create)

    • 使用 create 方法來新增並保存記錄。
    1
    article = Article.create(title: 'New Article', content: 'This is the content of the article')
    • 使用 create! 方法也用於新增記錄並保存到資料庫,不過行為不同。如果新增成功,會返回新記錄的實例,但如果出現任何問題,會引發異常(通常是 ActiveRecord::RecordInvalid),並中止操作。

    • 使用 new 方法新增一個實例,然後調用 save 方法來保存記錄。

    1
    2
    article = Article.new(title: 'New Article', content: 'This is the content of the article')
    article.save

    newcreate 的差別:new 方法只是先把物件做出來,尚未存入資料表,因此要手動透過 save 儲存;而 create 方法則是直接把存入資料表。

  2. 讀取(Read)

    • 使用 find 方法按主鍵(ID)查找記錄。
    1
    article = Article.find(1)
    • 使用 where 方法按特定條件查找多個記錄。
    1
    articles = Article.where(category: 'Technology')
    • 使用 all 方法獲取所有記錄。
    1
    all_articles = Article.all
  3. 更新(Update)

    • 使用 update 方法來更新記錄。
    1
    2
    article = Article.find(1)
    article.update(title: 'Updated Article Title', content: 'Updated content')
  4. 刪除(Delete)

    • 使用 destroy 方法刪除單個記錄。
    1
    2
    article = Article.find(1)
    article.destroy
    • 使用 delete 方法刪除單個記錄,但不執行回呼或驗證。
    1
    2
    article = Article.find(1)
    article.delete
    • 使用 destroy_all 方法刪除多個記錄。
    1
    2
    articles = Article.where(category: 'Obsolete')
    articles.destroy_all
    • 使用 delete_all 方法刪除多個記錄,但不執行回呼或驗證。
    1
    2
    articles = Article.where(category: 'Obsolete')
    articles.delete_all

Callbacks 回呼

Active Record 回呼(Callbacks)可以在 Model 的生命週期中定義一些方法,這些方法會在特定事件發生時自動執行。這些事件包括記錄的新增、更新、刪除等。
Active Record 回呼在這些事件之前、之後或在其他特定情況下執行自定義的程式碼,像是:數據驗證、處理圖片上傳、設定默認值、發送通知等。

以下是一些常見的 Active Record 回呼事件:

  1. before_save 和 after_save

    • before_save 回呼在記錄保存到資料庫之前執行,通常用於數據驗證、設定默認值等操作。
    • after_save 回呼在記錄成功保存到資料庫之後執行,通常用於記錄日誌、發送通知等操作。
  2. before_create 和 after_create

    • before_create 回呼在創建新記錄之前執行。
    • after_create 回呼在成功創建新記錄之後執行。
  3. before_update 和 after_update

    • before_update 回呼在更新記錄之前執行。
    • after_update 回呼在成功更新記錄之後執行。
  4. before_destroy 和 after_destroy

    • before_destroy 回呼在刪除記錄之前執行。
    • after_destroy 回呼在成功刪除記錄之後執行。

回呼也可以自定義邏輯,例如,你可以使用 before_save 回呼來檢查數據的有效性,或者使用 after_create 回呼來發送一封歡迎郵件給新註冊的用戶。
以下來看另外一個舉例,在 User 模型中定義了一個 before_save 回呼,在保存記錄之前檢查 api_key 是否為空,如果為空,則生成一個新的 API 金鑰。

1
2
3
4
5
6
7
8
9
class User < ApplicationRecord
before_save :generate_api_key

private

def generate_api_key
self.api_key = SecureRandom.hex(16) if api_key.blank?
end
end

Migration 遷移

Migration 遷移是一個描述資料庫的架構長什麼樣子的檔案。
每個遷移透過逐步添加、修改或刪除資料表、欄位和記錄等,Active Record 能夠根據遷移的時間順序更新資料庫結構,使資料庫能夠在任何時間點前進到最新版本。
同時,Active Record 也會維護一個 db/schema.rb 檔案,以保持其與最新的資料庫結構同步。

在執行 rails db:migrate 後,資料表便隨之產生,
可以想到資料表會有 title 與 content 兩個欄位,但實際打開資料表會發現,
多了 id、created_at 跟 updated_at 這三個欄位。
其實在 Migration 檔案中的 t.timestamps,會產生 created_at 跟 updated_at 的時間欄位,分別會在資料「新增」及「更新」的時候,把當下的時間寫入,所以在 Rails 專案中處理資料的時候,大多不太需要煩惱時間的問題。

id 欄位是 Rails 自動幫每個資料表加的流水編號欄位,
這個欄位稱為資料表的主鍵(Primary Key)。
如果你不想要這個主鍵,可以在 Migration 加上 id: false 參數:

1
2
3
4
5
6
7
8
9
10
class CreateArticles < ActiveRecord::Migration[7.0]
def change
create_table :articles, id: false do |t| # id: false
t.string :title
t.text :content

t.timestamps
end
end
end

如果想在 Model 再多新增欄位呢?

想要在既有的 Model 內,再新增欄位的話,
透過 rails g migration add_subtitle_to_article 新增一個 migration 之後,
藉由新生成的 migration,加上 add_column 方法,
就可以對之前有建立的 Model (Article) 新增欄位!

1
2
3
4
5
class AddSubtitleToArticle < ActiveRecord::Migration[7.0]
def change
add_column :articles, :sub_title, :string
end
end

接著再次 rails db:migrate 後,就可以發現資料庫裡面新增了 sub_title 欄位了!


Brief Summary

Active Record 是 Rails 中的 ORM 實現,讓開發人員能夠以物件導向的方式處理資料,而不必直接處理 SQL 查詢。通過定義模型(Model),我們可以執行 CRUD 操作(新增、讀取、更新、刪除),同時使用回呼(Callbacks)來自動執行特定事件。Migration 則允許我們管理資料庫結構的變化,保持資料庫和程式碼的同步。

詳細的內容可以參考Active Record 基礎看更深入的細節!


參考資料:
為你自己學 Ruby on Rails - Model
[Rails] Active Record Basic(Model 基本的 CRUD 操作)
Active Record 基礎