更多內容 rubyonrails.org: 更多內容

Active Record 基礎

本篇介紹 Active Record。

讀完本篇,您將了解:

1 Active Record 是什麼?

Active Record 是 MVC 的 M(Model),表現商業邏輯與資料的層級。Active Record 負責新增與操作需要持久存在資料庫裡的資料。Active Record 本身是物件關聯映射(Object Relational Mapping)系統的描述,以 Active Record 模式實作。

1.1 Active Record 模式

Active Record 模式出自 Martin Fowler 在其書:《Patterns of Enterprise Application Architecture》中所描述的 Active Record。在 Active Record 模式裡,物件擁有持久化的資料與行為,Active Record 確保存取資料的邏輯是物件的一部分,進而教導使用者如何將物件寫入於讀出資料庫。

1.2 物件關聯映射

物件關聯映射,通常縮寫為 ORM。是一種技巧,將應用程式中複雜的物件,對應到關聯式資料庫管理系統中的資料表。使用 ORM,可以輕鬆儲存物件的特性與關係,取出來的時候也不需要撰寫 SQL 語句,總體上減少了與資料庫存取有關的程式碼。

1.3 Active Record 作為 ORM 框架

Active Record 賦予我們許多功能,最重要幾個是:

  • 表示 Model 與資料。
  • 表示 Model 之間的關係。
  • 表示相關 Model 之間的繼承關係。
  • 持久化資料存入資料庫的驗證。
  • 以物件導向的風格操作資料庫。

2 Active Record 中的慣例勝於設定

使用其它程式語言撰寫應用程式時,可能會需要寫許多與設定有關的程式碼。大多數的 ORM 框架都是這樣。然而如果依循 Rails 的慣例,新建 Active Record Model 便只需要非常少的設定(某些情況甚至無需設定)。背後的概念是,如果多數時候大家都這麼設定應用程式,那這應該是預設的設定方式。因此,再無法遵循標準慣例的情況下,才需要額外設定。

2.1 命名慣例

Active Record 預設使用某種命名慣例來找出 Model 與資料表的對應關係。Rails 會將類別名稱轉成複數來找到對應的資料表。所以 Book 類對應的資料表便叫做 books。Rails 單複數轉換機制非常強大,能從單數轉複數、複數轉單數,單字的單複數形的不規則轉換,都能正確處理。類別名稱由兩個以上的單字組成時,Model 名稱應要遵循 Ruby 的命名慣例,採用駝峰式命名,而資料表名稱必須採用底線分隔。例子:

  • 資料表 - 複數形,由底線分隔多個單字。
  • Model 類別 - 單數形,第一個字母大寫。

    Model / Class Table / Schema
    Article articles
    LineItem line_items
    Deer deers
    Mouse mice
    Person people

2.2 資料庫綱要慣例

Active Record 資料表欄位的命名慣例,取決於欄位的用途

  • 外鍵 - 應用資料表的單數形加上 _id 來命名,比如 item_id, order_id。Active Record 會在你建立 Model 之間的關聯時,尋找這種形式的欄位 singularized_table_name_id

  • 主鍵 - Active Record 預設會使用一個叫做 id 的整數欄位,作為資料表的主鍵。採用 Active Record 遷移 來建立資料表時,這個欄位會自動產生。

以下是某些選擇性的欄位名稱,會加入更多功能到 Active Record 實體:

  • created_at - 記錄首次建立時自動設定此欄位為當下的日期與時間。
  • updated_at - 無論何時更新記錄時,會自動設定此欄位為當下的日期與時間。
  • lock_version - 加入 optimistic locking 功能至 Model。
  • type - 表示 Model 開啟了單表繼承功能。
  • (association_name)_type - 儲存 多態關聯 所需的類型資料。
  • (table_name)_count - 用來快取關聯物件的數量。舉例來說,Article Model 的 comments_count 便會為每篇文章快取評論的數量。

雖然這些欄位名稱是選擇性的,但實際上是 Active Record 的保留字。如果要使用這些額外的功能,不要將這些保留字作為他用。比如,type 是用來設計單表繼承的資料表。如果沒有使用 STI 功能,試試用個類似的名稱如,“context” 來描述您在建模的資料。

3 新增 Active Record Models

新增 Active Record Model 非常簡單。只需要建立一個 ActiveRecord::Base 的子類別即可:

class Product < ActiveRecord::Base
end

便會新增一個 Product Model,對應到資料庫的 products 表。資料表當中的每一列,皆會對應到 Model 實體的屬性。假設 products 以下面的 SQL 語句新建而成:

CREATE TABLE products (
   id int(11) NOT NULL auto_increment,
   name varchar(255),
   PRIMARY KEY  (id)
);

按照上述的資料表綱要,可以寫出如下程式碼:

p = Product.new
p.name = "Some Book"
puts p.name # "Some Book"

4 覆寫命名慣例

那要是需要不同於 Active Record 所提供的命名慣例怎麼辦?或者是 Rails 應用程式使用的資料來自老舊的資料庫?沒問題,覆寫預設的慣例非常簡單。

可以使用 ActiveRecord::Base.table_name= 方法來指定對應的資料表名稱:

class Product < ActiveRecord::Base
  self.table_name = "PRODUCT"
end

如果修改了資料表的名稱,在測試裡會需要使用 set_fixture_class 來手動定義 fixture 的類別名稱。

class FunnyJoke < ActiveSupport::TestCase
  set_fixture_class funny_jokes: Joke
  fixtures :funny_jokes
  ...
end

覆寫資料表中的欄位也是有可能的,比如使用 ActiveRecord::Base.primary_key= 方法將修改主鍵的名稱

class Product < ActiveRecord::Base
  self.primary_key = "product_id"
end

5 CRUD:讀寫資料

CRUD 是四種資料操作的簡稱:Create, Read, Update and Delete,分別是新增、讀取、更新與刪除。Active Record 自動為應用程式新增處理資料表所需要的方法。

5.1 新增 Create

Active Record 物件可以從 Hash、區塊(blcok)中建立出來,或者是建立後再設定也可以。new 方法回傳一個新的物件,而 create 會會傳新物件並存入資料庫。

舉個例子,User Model 有 nameoccupation 屬性,以下是用 create 方法在資料庫新增一筆記錄的例子:

user = User.create(name: "David", occupation: "Code Artist")

使用 new 方法,物件會實體化出來,但不會儲存:

user = User.new
user.name = "David"
user.occupation = "Code Artist"

呼叫 user.save 會將該筆記錄存入資料庫。

最後,使用區塊的例子,會將 User.new 實體化出來的物件放入區塊裡,對個別屬性作設定:

user = User.new do |u|
  u.name = "David"
  u.occupation = "Code Artist"
end

5.2 讀取 Read

Active Record 提供了豐富的 API 來存取資料庫裡的資料。下面是 Active Record 所提供的幾個資料存取方法用例:

# return a collection with all users
users = User.all

# return the first user
user = User.first

# return the first user named David
david = User.find_by(name: 'David')

# find all users named David who are Code Artists and sort by created_at in reverse chronological order
users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC')

關於對 Active Record Model 做查詢的內容,請參考 Active Record Query Interface

5.3 更新 Update

一旦 Active Record 物件被取出來了,就可以對屬性修改,再存回資料庫。

user = User.find_by(name: 'David')
user.name = 'Dave'
user.save

修改屬性再儲存有簡寫方式,使用 Hash 來對應要修改的屬性,如下所示:

user = User.find_by(name: 'David')
user.update(name: 'Dave')

一次更新多個屬性時用這招最有效。若是要批量更新多筆記錄,可以使用類別方法:update_all

User.update_all "max_login_attempts = 3, must_change_password = 'true'"

5.4 刪除 Delete

既然可以取出 Active Record 物件做更新,同樣也可以將其從資料庫移除。

user = User.find_by(name: 'David')
user.destroy

6 驗證

Active Record 允許您在資料被存入資料庫之前,驗證資料的狀態。驗證有許多方法,比如可以檢查屬性的值是不是空的、是不是唯一的、資料庫裡是不是已經有一份?每種檢查方法有特定的書寫格式。

驗證是在把持久化資料存入資料庫前,需要審慎思量的問題。跟資料存入資料庫有關的二種方法 save 以及 update,在呼叫時會進行驗證。當這三個方法回傳值為 false 時,驗證失敗,將不會對資料庫進行任何操作。上述三個方法皆有對應的 BANG 方法:save! 以及 update!,這比原本的方法更嚴格些,一旦失敗會直接拋出 ActiveRecord::RecordInvalid 的異常。用個簡單例子來說明:

class User < ActiveRecord::Base
  validates :name, presence: true
end

user = User.new
user.save  # => false
user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank

了解更多關於驗證的內容,請參考:Active Record Validations guide

7 回呼(Callbacks)

Active Record 回呼允許您在 Model 生命週期裡對特定事件附加程式碼。這使您可以在特定事件發生時,執行特定的程式碼。比如向資料庫新增、更新、刪除某筆記錄等。了解更多關於回呼的內容,請參考:Active Record Callbacks

8 遷移

Rails 提供了用來處理資料庫綱要的 DSL,稱為“遷移”。遷移存在檔案裡,可以對 Active Record 支持的任何資料庫,透過 rake 執行。以下是如何新建一張資料表:

class CreatePublications < ActiveRecord::Migration
  def change
    create_table :publications do |t|
      t.string :title
      t.text :description
      t.references :publication_type
      t.integer :publisher_id
      t.string :publisher_type
      t.boolean :single_issue

      t.timestamps null: false
    end
    add_index :publications, :publication_type_id
  end
end

Rails 持續追蹤提交到資料庫的檔案,並提供回滾功能。要真正的建立一張資料表,需要執行:rake db:migrate;要回滾則是執行:rake db:rollback

注意以上的程式碼適用於任何資料庫,不管是 Oracle、PostgreSQL、MySQL 都可以。了解更多關於遷移的內容,請參考 Active Record Migrations

反饋

歡迎幫忙改善指南的品質。

如發現任何錯誤之處,歡迎修正。開始貢獻前,可以先閱讀貢獻指南:文件

翻譯如有錯誤,深感抱歉,歡迎 Fork 修正,或至此處回報

文章可能有未完成或過時的內容。請先檢查 Edge Guides 來確定問題在 master 是否已經修掉了。再上 master 補上缺少的文件。內容參考 Ruby on Rails 指南準則來了解行文風格。

最後,任何關於 Ruby on Rails 文件的討論,歡迎至 rubyonrails-docs 郵件論壇