edge
更多資訊請參考 rubyonrails.org: 更多 Ruby on Rails

Active Record 基礎

本指南是 Active Record 的介紹。

閱讀完本指南後,您將了解以下內容:

1 什麼是 Active Record?

Active Record 是 MVC 中的 M,即模型(model),它是系統的一個層面,負責表示業務數據和邏輯。Active Record 促進了創建和使用需要將數據持久存儲到數據庫中的業務對象。它是 Active Record 模式的一種實現,而 Active Record 模式本身是對對象關聯映射系統的描述。

1.1 Active Record 模式

Active Record 是由 Martin Fowler 在他的書《企業應用架構模式》中描述的。在 Active Record 中,對象既包含持久數據,也包含操作該數據的行為。Active Record 的觀點是,將數據訪問邏輯作為對象的一部分,可以教育使用該對象的用戶如何向數據庫寫入和讀取數據。

1.2 物件關聯映射

物件關聯映射(Object Relational Mapping),通常簡稱為 ORM,是一種將應用程序中豐富的對象與關聯數據庫管理系統中的表格相連接的技術。使用 ORM,應用程序中對象的屬性和關係可以輕鬆地存儲和檢索,而無需直接編寫 SQL 語句,並且需要較少的數據庫訪問代碼。

注意:瞭解關聯數據庫管理系統(RDBMS)和結構化查詢語言(SQL)的基本知識有助於充分理解 Active Record。如果您想要進一步學習,請參考 這個教程(或 這個教程),或以其他方式學習。

1.3 Active Record 作為 ORM 框架

Active Record 提供了幾個機制,其中最重要的是能夠:

  • 表示模型及其數據。
  • 表示這些模型之間的關聯。
  • 通過相關模型表示繼承層次結構。
  • 在將模型持久存儲到數據庫之前驗證模型。
  • 以面向對象的方式執行數據庫操作。

2 Active Record 中的配置優於約定

在使用其他編程語言或框架編寫應用程序時,可能需要編寫大量的配置代碼。這對於一般的 ORM 框架尤其如此。然而,如果您遵循 Rails 採用的約定,創建 Active Record 模型時將只需要編寫非常少量的配置(在某些情況下甚至不需要配置)。這個想法是,如果您大部分時間都按照相同的方式配置應用程序,那麼這應該是默認方式。因此,只有在無法遵循標準約定的情況下才需要顯式配置。

2.1 命名慣例

默認情況下,Active Record 使用一些命名慣例來確定模型和數據庫表之間的映射方式。Rails 會將您的類名轉為複數形式以找到相應的數據庫表。因此,對於一個名為 Book 的類,您應該有一個名為 books 的數據庫表。Rails 的複數形式機制非常強大,能夠對常規和不規則的單詞進行複數化(和單數化)。當使用由兩個或多個單詞組成的類名時,模型類名應遵循 Ruby 的命名慣例,使用 CamelCase 形式,而表名必須使用 snake_case 形式。例如:

  • 模型類 - 單數形式,每個單詞的首字母大寫(例如,BookClub)。
  • 數據庫表 - 複數形式,單詞之間用下劃線分隔(例如,book_clubs)。
模型 / 類名 表格 / 架構名稱
Article articles
LineItem line_items
Deer deers
Mouse mice
Person people

2.2 架構慣例

Active Record 使用命名慣例來命名數據庫表中的列,具體取決於這些列的用途。

  • 外鍵 - 這些字段的命名應遵循 單數形式的表名_id 的模式(例如,item_idorder_id)。這些字段是 Active Record 在創建模型之間的關聯時尋找的字段。
  • 主鍵 - 默認情況下,Active Record 將使用一個名為 id 的整數列作為表的主鍵(對於 PostgreSQL 和 MySQL 使用 bigint,對於 SQLite 使用 integer)。使用 Active Record 遷移 創建表時,將自動創建此列。 還有一些可選的欄位名稱,可以為Active Record實例添加額外的功能:

  • created_at - 在記錄首次創建時自動設置為當前日期和時間。

  • updated_at - 在記錄創建或更新時自動設置為當前日期和時間。

  • lock_version - 為模型添加樂觀鎖定

  • type - 指定模型使用單表繼承

  • (association_name)_type - 存儲多態關聯的類型。

  • (table_name)_count - 用於緩存關聯上的對象數量。例如,Article類中有許多Comment實例的comments_count列將緩存每篇文章的現有評論數量。

注意:雖然這些欄位名稱是可選的,但實際上它們是由Active Record保留的。除非您需要額外的功能,否則應避免使用保留關鍵字。例如,type是用於指定使用單表繼承(STI)的表的保留關鍵字。如果您不使用STI,可以嘗試使用類似的關鍵字,如“context”,這可能仍然準確描述您正在建模的數據。

3 創建Active Record模型

在生成應用程序時,將在app/models/application_record.rb中創建一個抽象的ApplicationRecord類。這是應用程序中所有模型的基類,它將一個普通的Ruby類轉換為Active Record模型。

要創建Active Record模型,請將其子類化為ApplicationRecord類,然後就可以使用了:

class Product < ApplicationRecord
end

這將創建一個Product模型,將其映射到數據庫中的products表。通過這樣做,您還可以將該表中每一行的列與模型實例的屬性進行映射。假設products表是使用SQL(或其擴展之一)語句創建的,如下所示:

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

上面的模式聲明了一個具有兩個列idname的表。該表的每一行表示具有這兩個參數的某個產品。因此,您可以編寫以下代碼:

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

4 覆蓋命名慣例

如果您需要遵循不同的命名慣例或需要使用遺留數據庫與Rails應用程序,則可以輕鬆覆蓋默認慣例。

由於ApplicationRecord繼承自ActiveRecord::Base,因此應用程序的模型將具有許多有用的方法可供使用。例如,您可以使用ActiveRecord::Base.table_name=方法自定義要使用的表名:

class Product < ApplicationRecord
  self.table_name = "my_products"
end

如果這樣做,您將需要在測試定義中使用set_fixture_class方法手動定義托管測試數據(my_products.yml)的類名:

# test/models/product_test.rb
class ProductTest < ActiveSupport::TestCase
  set_fixture_class my_products: Product
  fixtures :my_products
  # ...
end

還可以使用ActiveRecord::Base.primary_key=方法覆蓋作為表的主鍵使用的列:

class Product < ApplicationRecord
  self.primary_key = "product_id"
end

注意:Active Record不支持使用非主鍵列命名為id

注意:如果您嘗試創建一個名為id的列,而該列不是主鍵,Rails將在遷移期間拋出錯誤,例如:you can't redefine the primary key column 'id' on 'my_products'. To define a custom primary key, pass { id: false } to create_table.

5 CRUD:讀取和寫入數據

CRUD是我們用於操作數據的四個動詞的首字母縮寫:Create(創建)、Read(讀取)、Update(更新)和Delete(刪除)。Active Record會自動創建方法,允許應用程序讀取和操作存儲在其表中的數據。

5.1 創建

可以從哈希、塊或在創建後手動設置其屬性的方式來創建Active Record對象。new方法將返回一個新對象,而create將返回該對象並將其保存到數據庫中。

例如,假設有一個名為User的模型,具有nameoccupation兩個屬性,則create方法調用將創建並將一條新記錄保存到數據庫中:

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

使用new方法可以實例化一個對象而不保存:

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

調用user.save將將記錄提交到數據庫。

最後,如果提供了一個塊,createnew都將將新對象傳遞給該塊進行初始化,而只有create會將結果對象持久化到數據庫:

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

5.2 讀取

Active Record提供了一個豐富的API來訪問數據庫中的數據。以下是Active Record提供的幾種不同數據訪問方法的示例。

# 返回包含所有用戶的集合
users = User.all
# 返回第一個用戶
user = User.first
# 返回名為David的第一個用戶
david = User.find_by(name: 'David')
# 查找所有名為David且職業為Code Artist的用戶,按照創建時間倒序排序
users = User.where(name: 'David', occupation: 'Code Artist').order(created_at: :desc)

您可以在Active Record查詢接口指南中了解更多關於查詢Active Record模型的信息。

5.3 更新

一旦檢索到Active Record對象,可以修改其屬性並將其保存到數據庫。

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

一種簡寫方式是使用將屬性名映射到所需值的哈希,如下所示:

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 刪除

同樣地,一旦檢索到Active Record對象,可以刪除它,從數據庫中刪除它。

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

如果您想要批量刪除多個記錄,可以使用destroy_bydestroy_all方法:

# 查找並刪除所有名為David的用戶
User.destroy_by(name: 'David')

# 刪除所有用戶
User.destroy_all

6 驗證

Active Record允許您在將模型寫入數據庫之前驗證模型的狀態。有幾種方法可以檢查模型並驗證屬性值是否為空,是否唯一且不在數據庫中,是否遵循特定格式等等。

savecreateupdate等方法在將模型持久化到數據庫之前對模型進行驗證。當模型無效時,這些方法返回false,並且不執行任何數據庫操作。所有這些方法都有一個bang對應方法(即save!create!update!),它們更嚴格,當驗證失敗時引發ActiveRecord::RecordInvalid異常。以下是一個快速示例:

class User < ApplicationRecord
  validates :name, presence: true
end
irb> user = User.new
irb> user.save
=> false
irb> user.save!
ActiveRecord::RecordInvalid: Validation failed: Name can’t be blank

您可以在Active Record驗證指南中了解更多關於驗證的信息。

7 回調函數

Active Record回調函數允許您將代碼附加到模型的某些事件上。這使您可以通過在發生這些事件時透明地執行代碼來為模型添加行為,例如在創建新記錄、更新記錄、刪除記錄等時。

class User < ApplicationRecord
  after_create :log_new_user

  private
    def log_new_user
      puts "A new user was registered"
    end
end
irb> @user = User.create
A new user was registered

您可以在Active Record回調指南中了解更多關於回調函數的信息。

8 遷移

Rails提供了一種方便的方式來通過遷移管理對數據庫模式的更改。遷移使用特定於域的語言編寫,並存儲在對Active Record支持的任何數據庫上執行的文件中。

以下是創建一個名為publications的新表的遷移示例:

class CreatePublications < ActiveRecord::Migration[7.1]
  def change
    create_table :publications do |t|
      t.string :title
      t.text :description
      t.references :publication_type
      t.references :publisher, polymorphic: true
      t.boolean :single_issue

      t.timestamps
    end
  end
end

請注意,上述代碼是與數據庫無關的:它可以在MySQL、PostgreSQL、SQLite和其他數據庫中運行。

Rails跟踪已提交到數據庫的遷移並將它們存儲在同一數據庫中的相鄰表schema_migrations中。 要運行遷移並創建表,您需要運行 bin/rails db:migrate, 要回滾並刪除表,則運行 bin/rails db:rollback

您可以在Active Record遷移指南中了解更多關於遷移的資訊。

9 關聯

Active Record關聯允許您定義模型之間的關係。 關聯可用於描述一對一、一對多和多對多的關係。例如,像“作者有多本書”這樣的關係可以定義如下:

class Author < ApplicationRecord
  has_many :books
end

現在,Author類別具有添加和刪除作者的書籍等方法。

您可以在Active Record關聯指南中了解更多關於關聯的資訊。

回饋

歡迎協助提升本指南的品質。

如果您發現任何錯別字或事實錯誤,請貢獻您的力量。 開始之前,您可以閱讀我們的 文件貢獻 部分。

您也可能會發現不完整的內容或過時的資訊。 請為主要的文件補充任何遺漏的內容。請先檢查 Edge 指南,以確認問題是否已經修復或尚未在主分支上修復。 請參考 Ruby on Rails 指南指引 以了解風格和慣例。

如果您發現需要修復但無法自行修補的問題,請 開啟一個問題

最後但同樣重要的是,關於 Ruby on Rails 文件的任何討論都非常歡迎在 官方 Ruby on Rails 論壇 上進行。