1. 程式人生 > >Active Record 基礎

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_idorder_id。建立模型關聯後,Active Record 會查詢這個欄位;
  • 主鍵:預設情況下,Active Record 使用整數字段 id
     作為表的主鍵。使用 Active Record 遷移建立資料庫表時,會自動建立這個欄位;

還有一些可選的欄位,能為 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 類就行了:

class Product < ApplicationRecord
end

上面的程式碼會建立 Product 模型,對應於資料庫中的 products 表。同時,products 表中的欄位也對映到 Product 模型例項的屬性上。

假如 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"

建立

呼叫 create 方法會建立一個新記錄,並將其存入資料庫:

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

new 方法例項化一個新物件,但不儲存:

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

呼叫 user.save 可以把記錄存入資料庫。

最後,如果在 create 和 new 方法中使用塊,會把新建立的物件拉入塊中,初始化物件:

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

 

Active Record 資料驗證:

 

資料驗證概覽

下面是一個非常簡單的資料驗證:

class Person < ApplicationRecord
  validates :name, presence: true
end
 
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false

可以看出,如果 Person 沒有 name 屬性,驗證就會將其視為無效物件。第二個 Person 物件不會存入資料庫。

為什麼要做資料驗證?

資料驗證確保只有有效的資料才能存入資料庫。例如,應用可能需要使用者提供一個有效的電子郵件地址和郵寄地址。在模型中做驗證是最有保障的,只有通過驗證的資料才能存入資料庫。資料驗證和使用的資料庫種類無關,終端使用者也無法跳過,而且容易測試和維護。在 Rails 中做資料驗證很簡單,Rails 內建了很多輔助方法,能滿足常規的需求,而且還可以編寫自定義的驗證方法。

在資料存入資料庫之前,也有幾種驗證資料的方法,包括資料庫原生的約束、客戶端驗證和控制器層驗證。下面列出這幾種驗證方法的優缺點:

  • 資料庫約束和儲存過程無法相容多種資料庫,而且難以測試和維護。然而,如果其他應用也要使用這個資料庫,最好在資料庫層做些約束。此外,資料庫層的某些驗證(例如在使用量很高的表中做唯一性驗證)通過其他方式實現起來有點困難。
  • 客戶端驗證很有用,但單獨使用時可靠性不高。如果使用 JavaScript 實現,使用者在瀏覽器中禁用 JavaScript 後很容易跳過驗證。然而,客戶端驗證和其他驗證方式相結合,可以為使用者提供實時反饋。
  • 控制器層驗證很誘人,但一般都不靈便,難以測試和維護。只要可能,就要保證控制器的程式碼簡潔,這樣才有利於長遠發展。

你可以根據實際需求選擇使用合適的驗證方式。Rails 團隊認為,模型層資料驗證最具普適性

 

資料在何時驗證?

 

Active Record 物件分為兩種:一種在資料庫中有對應的記錄,一種沒有。新建的物件(例如,使用 new 方法)還不屬於資料庫。在物件上呼叫 save 方法後,才會把物件存入相應的資料庫表。Active Record 使用例項方法 new_record? 判斷物件是否已經存入資料庫。假如有下面這個簡單的 Active Record 類:

 

class Person < ApplicationRecord
end

 

我們可以在 rails console 中看一下到底怎麼回事:

 

$ bin/rails console
>> p = Person.new(name: "John Doe")
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_資料驗證:at: nil>
>> p.new_record?
=> true
>> p.save
=> true
>> p.new_record?
=> false

 

新建並儲存記錄會在資料庫中執行 SQL INSERT 操作。更新現有的記錄會在資料庫中執行 SQL UPDATE 操作。一般情況下,資料驗證發生在這些 SQL 操作執行之前。如果驗證失敗,物件會被標記為無效,Active Record 不會向資料庫傳送 INSERT 或 UPDATE 指令。這樣就可以避免把無效的資料存入資料庫。你可以選擇在物件建立、儲存或更新時執行特定的資料驗證。

 

注意:修改資料庫中物件的狀態有多種方式。有些方法會觸發資料驗證,有些則不會。所以,如果不小心處理,還是有可能把無效的資料存入資料庫。

下列方法會觸發資料驗證,如果驗證失敗就不把物件存入資料庫:

  • create
  • create!
  • save
  • save!
  • update
  • update!

爆炸方法(例如 save!)會在驗證失敗後丟擲異常。驗證失敗後,非爆炸方法不會丟擲異常,save 和 update 返回 falsecreate 返回物件本身。

跳過驗證

下列方法會跳過驗證,不管驗證是否通過都會把物件存入資料庫,使用時要特別留意。

  • 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。前面我們已經用過了:

class Person < ApplicationRecord
  validates :name, presence: true
end
 
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false

Active Record 執行驗證後,所有發現的錯誤都可以通過例項方法 errors.messages 獲取。該方法返回一個錯誤集合。如果資料驗證後,這個集合為空,說明物件是有效的。

注意,使用 new 方法初始化物件時,即使無效也不會報錯,因為只有儲存物件時才會驗證資料,例如呼叫 create 或 save 方法。