Active Record 基礎
Active Record 是 MVC 中的 M(模型),負責處理資料和業務邏輯。Active Record 負責建立和使用需要持久存入資料庫中的資料。Active Record 實現了 Active Record 模式,是一種物件關係對映系統。
Active Record 模式
在 Active Record 模式中,物件中既有持久儲存的資料,也有針對資料的操作。Active Record 模式把資料存取邏輯作為物件的一部分,處理物件的使用者知道如何把資料寫入資料庫,還知道如何從資料庫中讀出資料。'
物件關係對映
物件關係對映(ORM)是一種技術手段,把應用中的物件和關係型資料庫中的資料表連線起來。使用 ORM,應用中物件的屬性和物件之間的關係可以通過一種簡單的方法從資料庫中獲取,無需直接編寫 SQL 語句,也不過度依賴特定的資料庫種類。
用作 ORM 框架的 Active Record
- 表示模型和其中的資料;
- 表示模型之間的關係;
- 通過相關聯的模型表示繼承層次結構;
- 持久存入資料庫之前,驗證模型;
- 以面向物件的方式處理資料庫操作。
Active Record 中的“多約定少配置”原則
使用其他程式語言或框架開發應用時,可能必須要編寫很多配置程式碼。大多數 ORM 框架都是這樣。但是,如果遵循 Rails 的約定,建立 Active Record 模型時不用做多少配置(有時甚至完全不用配置)。Rails 的理念是,如果大多數情況下都要使用相同的方式配置應用,那麼就應該把這定為預設的方式。所以,只有約定無法滿足要求時,才要額外配置。
命名約定
- 資料庫表名:複數,下劃線分隔單詞(例如
book_clubs
) - 模型類名:單數,每個單詞的首字母大寫(例如
BookClub
)
模式約定
根據欄位的作用不同,Active Record 對資料庫表中的欄位命名也做了相應的約定:
- 外來鍵:使用
singularized_table_name_id
形式命名,例如item_id
,order_id
。建立模型關聯後,Active Record 會查詢這個欄位; - 主鍵:預設情況下,Active Record 使用整數字段
id
還有一些可選的欄位,能為 Active Record 例項新增更多的功能:
created_at
:建立記錄時,自動設為當前的日期和時間;updated_at
:更新記錄時,自動設為當前的日期和時間;lock_version
:在模型中新增樂觀鎖;type
:讓模型使用單表繼承;(association_name)_type
:儲存多型關聯的型別;(table_name)_count
:快取所關聯物件的數量。比如說,一個Article
有多個Comment
,那麼comments_count
列儲存各篇文章現有的評論數量;
然這些欄位是可選的,但在 Active Record 中是被保留的。如果想使用相應的功能,就不要把這些保留欄位用作其他用途。例如,type
這個保留欄位是用來指定資料庫表使用單表繼承(Single Table Inheritance,STI)的。如果不用單表繼承,請使用其他的名稱,例如“context”,這也能表明資料的作用。
建立 Active Record 模型
建立 Active Record 模型的過程很簡單,只要繼承 ApplicationRecord
類就行了:
|
上面的程式碼會建立 Product
模型,對應於資料庫中的 products
表。同時,products
表中的欄位也對映到 Product
模型例項的屬性上。
假如 products
表由下面的 SQL 語句建立:
|
按照這樣的資料表結構,可以編寫下面的程式碼:
|
建立
呼叫 create
方法會建立一個新記錄,並將其存入資料庫:
|
new
方法例項化一個新物件,但不儲存:
|
呼叫 user.save
可以把記錄存入資料庫。
最後,如果在 create
和 new
方法中使用塊,會把新建立的物件拉入塊中,初始化物件:
|
Active Record 資料驗證:
資料驗證概覽
下面是一個非常簡單的資料驗證:
|
可以看出,如果 Person
沒有 name
屬性,驗證就會將其視為無效物件。第二個 Person
物件不會存入資料庫。
為什麼要做資料驗證?
資料驗證確保只有有效的資料才能存入資料庫。例如,應用可能需要使用者提供一個有效的電子郵件地址和郵寄地址。在模型中做驗證是最有保障的,只有通過驗證的資料才能存入資料庫。資料驗證和使用的資料庫種類無關,終端使用者也無法跳過,而且容易測試和維護。在 Rails 中做資料驗證很簡單,Rails 內建了很多輔助方法,能滿足常規的需求,而且還可以編寫自定義的驗證方法。
在資料存入資料庫之前,也有幾種驗證資料的方法,包括資料庫原生的約束、客戶端驗證和控制器層驗證。下面列出這幾種驗證方法的優缺點:
- 資料庫約束和儲存過程無法相容多種資料庫,而且難以測試和維護。然而,如果其他應用也要使用這個資料庫,最好在資料庫層做些約束。此外,資料庫層的某些驗證(例如在使用量很高的表中做唯一性驗證)通過其他方式實現起來有點困難。
- 客戶端驗證很有用,但單獨使用時可靠性不高。如果使用 JavaScript 實現,使用者在瀏覽器中禁用 JavaScript 後很容易跳過驗證。然而,客戶端驗證和其他驗證方式相結合,可以為使用者提供實時反饋。
- 控制器層驗證很誘人,但一般都不靈便,難以測試和維護。只要可能,就要保證控制器的程式碼簡潔,這樣才有利於長遠發展。
你可以根據實際需求選擇使用合適的驗證方式。Rails 團隊認為,模型層資料驗證最具普適性
資料在何時驗證?
Active Record 物件分為兩種:一種在資料庫中有對應的記錄,一種沒有。新建的物件(例如,使用 new
方法)還不屬於資料庫。在物件上呼叫 save
方法後,才會把物件存入相應的資料庫表。Active Record 使用例項方法 new_record?
判斷物件是否已經存入資料庫。假如有下面這個簡單的 Active Record 類:
|
我們可以在 rails console
中看一下到底怎麼回事:
|
新建並儲存記錄會在資料庫中執行 SQL INSERT
操作。更新現有的記錄會在資料庫中執行 SQL UPDATE
操作。一般情況下,資料驗證發生在這些 SQL 操作執行之前。如果驗證失敗,物件會被標記為無效,Active Record 不會向資料庫傳送 INSERT
或 UPDATE
指令。這樣就可以避免把無效的資料存入資料庫。你可以選擇在物件建立、儲存或更新時執行特定的資料驗證。
注意:修改資料庫中物件的狀態有多種方式。有些方法會觸發資料驗證,有些則不會。所以,如果不小心處理,還是有可能把無效的資料存入資料庫。
下列方法會觸發資料驗證,如果驗證失敗就不把物件存入資料庫:
create
create!
save
save!
update
update!
爆炸方法(例如 save!
)會在驗證失敗後丟擲異常。驗證失敗後,非爆炸方法不會丟擲異常,save
和 update
返回 false
,create
返回物件本身。
跳過驗證
下列方法會跳過驗證,不管驗證是否通過都會把物件存入資料庫,使用時要特別留意。
decrement!
decrement_counter
increment!
increment_counter
toggle!
touch
update_all
update_attribute
update_column
update_columns
update_counters
注意,使用 save
時如果傳入 validate: false
引數,也會跳過驗證。使用時要特別留意。
save(validate: false)
valid?
和 invalid?
Rails 在儲存 Active Record 物件之前驗證資料。如果驗證過程產生錯誤,Rails 不會儲存物件。
你還可以自己執行資料驗證。valid?
方法會觸發資料驗證,如果物件上沒有錯誤,返回 true
,否則返回 false
。前面我們已經用過了:
|
Active Record 執行驗證後,所有發現的錯誤都可以通過例項方法 errors.messages
獲取。該方法返回一個錯誤集合。如果資料驗證後,這個集合為空,說明物件是有效的。
注意,使用 new
方法初始化物件時,即使無效也不會報錯,因為只有儲存物件時才會驗證資料,例如呼叫 create
或 save
方法。