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