iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0

今天來點 Active Record!

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

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

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

使用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

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 定義在資料庫中創建一個對應的資料表,該資料表包括 titlecontent 兩個欄位,
    且檔名會包含時間戳記和表名的相關資訊,且包含 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
    

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

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

  1. 新增(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
    

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

  2. 讀取(Read)

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

    • 使用 update 方法來更新記錄。
    article = Article.find(1)
    article.update(title: 'Updated Article Title', content: 'Updated content')
    
  4. 刪除(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
    

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 金鑰。

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 參數:

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) 新增欄位!

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 基礎看更深入的細節!

今天就先到這,我們下篇見!


參考資料:

文章同步於個人部落格:Viiisit!(歡迎參觀 ୧ʕ•̀ᴥ•́ʔ୨)


上一篇
Day 18 - 理解 Ruby on Rails,Controller, Action, View 實作!
下一篇
Day 20 - 理解 Ruby on Rails,Active Record Associations(上)
系列文
從零開始,在 coding 路上的 30 個為什麼?不對!是無數個為什麼!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言