今天來點 Active Record!
在講述 Active Record 之前,我們先來建立一個基礎概念:ORM Object–relational mapping
Object–relational mapping (ORM) 物件關聯映射
物件關聯映射(ORM)是一種軟體設計模式,將資料庫中的數據映射到物件導向程式語言中的物件,使開發人員能夠使用物件導向的方式處理數據,而不必直接處理SQL查詢。
用一個圖書館管理系統的例子來說明不使用 ORM 和使用 ORM 之間的差異。
不使用 ORM 的圖書館管理系統:
在不使用 ORM 的情況下,需要手動建立 Table,並編寫 SQL 查詢將資料存到資料庫中或從資料庫中檢索資料。
建立 Table
1
2
3
4
5
6CREATE TABLE books (
id INT PRIMARY KEY,
title VARCHAR(255),
author VARCHAR(255),
publication_year INT
);編寫原生 SQL 查詢來插入書的資料
1
INSERT INTO books (title, author, publication_year) VALUES ('To Kill a Mockingbird', 'Harper Lee', 1960);
編寫原生 SQL 查詢來檢索書的資料
1
SELECT * FROM books WHERE author = 'Harper Lee';
在這種情況下,需要自己處理資料庫結構的細節和 SQL 查詢的編寫。
使用 ORM 的圖書館管理系統:
使用ORM,可以更輕鬆地處理資料,並將其映射到物件。
Active Record 定義一個書模型(Model)
1
2class Book < ApplicationRecord
end創建一本新書並保存到資料庫
1
2book = Book.new(title: 'To Kill a Mockingbird', author: 'Harper Lee', publication_year: 1960)
book.save查詢書的資料:
1
books_by_harper_lee = Book.where(author: 'Harper Lee')
使用 ORM,可以將資料庫表格映射到模型 (Model),並使用 Model 方法來執行資料庫操作,而不必編寫原生 SQL 查詢或擔心資料庫結構的詳細資訊,使得代碼更容易閱讀、維護和擴展。
在理解完 ORM 之後,接著來看看 Active Record 吧!
Active Record
Active Record 是一個具體的 ORM 實現。他提供了一種方式來定義和操作 Model,隱藏了資料庫操作的細節,允許開發人員使用物件導向語法來處理資料。Active Record 還提供了方法來執行數據庫查詢、新增、更新和刪除記錄,並建立了 Model 和資料表之間的映射關係,而 Model 在 Rails 中處理與資料庫的互動、商業邏輯、驗證。
接續 Active Record 的概念,讓我們來了解如何建立一個新的 Model(模型)並與資料庫關聯。
建立
Article
Model:命名慣例:Model 的命名是單數
1
rails generate model Article title:string content:text
也可以簡寫成:
1
rails g model Article title content:text
這個命令會生成一個新的 Model 文件
article.rb
,並創建一個對應的 Migration,用於創建資料表,在這裡會有個叫 articles 的資料表(table),該資料表包含 title 和 content 兩個欄位,分別是 string 和 text 型別。
Migration 檔名會包含時間戳記和表名的相關資訊,且包含create_table
方法,用於定義資料表的結構。確認 Model 定義:
app/models/article.rb
1
2
3class Article < ApplicationRecord
validates :title, presence: true
end定義
Article
的 Model,並在其中添加了一個驗證規則,要求title
欄位必須存在。Active Record 驗證是 Ruby on Rails 中的重要功能,允許在保存記錄到資料庫之前對數據進行驗證,以確保數據的完整性和一致性。
執行 Migration:
使用以下命令執行 Migration,以建立
articles
資料表:確認資料表欄位都符合自己的需求時,要執行 rails db:migrate 才會真正在資料庫中建立相應的資料表!
沒執行就會看到錯誤訊息:ActiveRecord::PendingMigrationError
!1
rails db:migrate
根據 Model 定義在資料庫中創建一個對應的資料表,該資料表包括
title
和content
兩個欄位,
且檔名會包含時間戳記和表名的相關資訊,且包含create_table
方法,用於定義資料表的結構。db/migrate/20231003042828_create_articles.rb
1
2
3
4
5
6
7
8
9
10
11
12class CreateArticles < ActiveRecord::Migration[7.0]
def change
create_table :articles do |t|
t.string :title
t.text :content
t.timestamps
# t.datetime :created_at
# t.datetime :updated_at # 可以思考這個欄位有無需要更新時間功能
end
end
end使用 Model 的方法:
如此,我們可以在 Rails 中使用
Article
Model 來處理文章相關的操作,例如新增、查詢、更新和刪除文章。1
2
3
4
5
6
7
8
9
10
11
12
13
14# 新增一篇文章
article = Article.new(title: 'Sample Title', content: 'Sample Content')
article.save
# 查詢文章
articles = Article.where(title: 'Sample Title')
# 更新文章
article = Article.find_by(title: 'Sample Title')
article.update(content: 'Updated Content')
# 刪除文章
article = Article.find_by(title: 'Sample Title')
article.destroy
CRUD 新增、讀取、更新與刪除
當使用 Active Record 在 Ruby on Rails 中進行 CRUD(新增、讀取、更新和刪除)操作時,通常使用以下方法:
新增(Create)
- 使用
create
方法來新增並保存記錄。
1
article = Article.create(title: 'New Article', content: 'This is the content of the article')
使用
create!
方法也用於新增記錄並保存到資料庫,不過行為不同。如果新增成功,會返回新記錄的實例,但如果出現任何問題,會引發異常(通常是 ActiveRecord::RecordInvalid),並中止操作。使用
new
方法新增一個實例,然後調用save
方法來保存記錄。
1
2article = Article.new(title: 'New Article', content: 'This is the content of the article')
article.savenew
跟create
的差別:new
方法只是先把物件做出來,尚未存入資料表,因此要手動透過save
儲存;而create
方法則是直接把存入資料表。- 使用
讀取(Read)
- 使用
find
方法按主鍵(ID)查找記錄。
1
article = Article.find(1)
- 使用
where
方法按特定條件查找多個記錄。
1
articles = Article.where(category: 'Technology')
- 使用
all
方法獲取所有記錄。
1
all_articles = Article.all
- 使用
更新(Update)
- 使用
update
方法來更新記錄。
1
2article = Article.find(1)
article.update(title: 'Updated Article Title', content: 'Updated content')- 使用
刪除(Delete)
- 使用
destroy
方法刪除單個記錄。
1
2article = Article.find(1)
article.destroy- 使用
delete
方法刪除單個記錄,但不執行回呼或驗證。
1
2article = Article.find(1)
article.delete- 使用
destroy_all
方法刪除多個記錄。
1
2articles = Article.where(category: 'Obsolete')
articles.destroy_all- 使用
delete_all
方法刪除多個記錄,但不執行回呼或驗證。
1
2articles = Article.where(category: 'Obsolete')
articles.delete_all- 使用
Callbacks 回呼
Active Record 回呼(Callbacks)可以在 Model 的生命週期中定義一些方法,這些方法會在特定事件發生時自動執行。這些事件包括記錄的新增、更新、刪除等。
Active Record 回呼在這些事件之前、之後或在其他特定情況下執行自定義的程式碼,像是:數據驗證、處理圖片上傳、設定默認值、發送通知等。
以下是一些常見的 Active Record 回呼事件:
before_save 和 after_save:
before_save
回呼在記錄保存到資料庫之前執行,通常用於數據驗證、設定默認值等操作。after_save
回呼在記錄成功保存到資料庫之後執行,通常用於記錄日誌、發送通知等操作。
before_create 和 after_create:
before_create
回呼在創建新記錄之前執行。after_create
回呼在成功創建新記錄之後執行。
before_update 和 after_update:
before_update
回呼在更新記錄之前執行。after_update
回呼在成功更新記錄之後執行。
before_destroy 和 after_destroy:
before_destroy
回呼在刪除記錄之前執行。after_destroy
回呼在成功刪除記錄之後執行。
回呼也可以自定義邏輯,例如,你可以使用 before_save
回呼來檢查數據的有效性,或者使用 after_create
回呼來發送一封歡迎郵件給新註冊的用戶。
以下來看另外一個舉例,在 User
模型中定義了一個 before_save
回呼,在保存記錄之前檢查 api_key
是否為空,如果為空,則生成一個新的 API 金鑰。
1 | class User < ApplicationRecord |
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 | class CreateArticles < ActiveRecord::Migration[7.0] |
如果想在 Model 再多新增欄位呢?
想要在既有的 Model 內,再新增欄位的話,
透過 rails g migration add_subtitle_to_article 新增一個 migration 之後,
藉由新生成的 migration,加上 add_column 方法,
就可以對之前有建立的 Model (Article) 新增欄位!
1 | class AddSubtitleToArticle < ActiveRecord::Migration[7.0] |
接著再次 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 基礎