Ruby on Rails: Active Record Associations (上)
before_action :前言 為什麼需要關聯性? 在 Rails 中,Active Record 關聯是指不同 Model 之間的連結與關係。 我們在一個專案裡,一定不只有一個 Model,會同時存在許多 Model, 在 Rails 裡,我們可以透過關聯將這些 Model 連結在一起,大致上...
before_action :前言
為什麼需要關聯性?
在 Rails 中,Active Record 關聯是指不同 Model 之間的連結與關係。
我們在一個專案裡,一定不只有一個 Model,會同時存在許多 Model, 在 Rails 裡,我們可以透過關聯將這些 Model 連結在一起,大致上歸納出以下優點:
Model 需要關聯性是為了更好地組織和管理資料,以及實現資料間的關聯和連結。
-
資料的結構化:通常一個應用程式涉及多個資料,彼此之間可能存在著很複雜的關係。 可以將這些資料的關係結構化,使得資料庫中的資料更容易理解和管理。
-
整合資料:資料往往存在著相互關聯的情況。例如:一個使用者有多個訂單。 可以將這些相關聯的資料整合在一起,使得查詢和操作資料更加方便。
-
簡化資料存取:在查詢資料時不需要手動撰寫複雜的 SQL 語句,而是使用 Rails 提供的簡單方法即可。這樣可以節省開發時間,並減少出錯的可能性。
-
維護性和擴展性:當應用程式需要新增新的資料實體或調整資料結構時,使用 Model 的關聯性可以輕鬆地進行修改而不影響其他部分的程式碼。
關聯種類
這種關聯性可以在資料庫層面建立表格之間的連結,使得資料之間可以相互關聯和互動, 進而簡化資料的查詢和操作。
Rails 支援以下六種關聯:
- belongs_to
- has_one
- has_many
- has_many :through
- has_one :through
- has_and_belongs_to_many
透過關聯而產生的方法,你可以在 Model 類別中指定關聯的類別和條件,Rails 將根據這些定義在資料庫層面建立對應的關聯。
一對一關聯
-
belongs_to 生成 Profile Model 的指令:
rails generate model Profile bio:text user:belongs_to(or)rails generate model Profile bio:text user:references--> 這樣就會在 Profile 資料表中生成user_id這個 foreign key- 例如,個人檔案只屬於一位使用者。
# Profile 模型 class Profile < ApplicationRecord belongs_to :user end -
has_one 生成 User Model 的指令:
rails generate model User name:string- 例如,一個使用者只有一個個人檔案。
# User 模型 class User < ApplicationRecord has_one :profile end當有
has_one :profile時,這個關聯會生成以下方法:profile -> user.profile # 找出用戶的個人檔案 profile= -> user.profile = profile # 將一個現有的個人檔案分配給用戶,以更改用戶的個人檔案。 build_profile -> user.build_profile(bio: 'About me') # 建立一個新個人檔案,但該個人檔案不會立即保存到資料庫中。 create_profile -> user.create_profile(bio: 'About me') # 建立一個新個人檔案,同時個人檔案會保存到資料庫中。 create_profile! -> user.create_profile!(bio: 'About me') # 當保存個人檔案時,如果存在驗證失敗或其他錯誤,將引發異常(exception),而非返回 false。 reload_profile -> user.reload_profile # 如果你修改了個人檔案,但希望獲取最新的數據,可以使用 reload_profile 方法。 -
has_one :through(透過一對一關聯): 生成 User Model 的指令:
rails generate model User name:string生成 Profile Model 的指令:rails generate model Profile bio:text生成 Account Model 的指令:rails generate model Account user:references–> 這樣就會在 Account 資料表中生成user_id這個 foreign key- 例如,一個使用者可以透過一個帳戶來存取其餘的資訊。
# User 模型 class User < ApplicationRecord has_one :account has_one :profile, through: :account end # Account 模型 class Account < ApplicationRecord belongs_to :user has_one :profile end # Profile 模型 class Profile < ApplicationRecord belongs_to :account end可以使用以下方法來建立與尋找關聯資料:
# 建立使用者 user = User.create(name: 'Viii') # 建立帳戶,並與使用者建立關聯 account = user.create_account # 建立個人檔案,並與帳戶建立關聯 profile = account.build_profile(bio: 'About me') # 保存關聯記錄到資料庫 user.save查找:
# 查找使用者的帳戶 account = user.account # 查找使用者的個人檔案(透過帳戶關聯) profile = user.profile # 查找帳戶所屬的使用者 account_user = account.user # 查找帳戶關聯的個人檔案 account_profile = account.profile
一對多關聯
-
has_many
- 例如,一個使用者有多篇文章。
# User 模型 class User < ApplicationRecord has_many :articles end # Article 模型 class Article < ApplicationRecord belongs_to :user end當有
has_many :articles時,這個關聯會自帶以下方法:articles -> user.articles # 獲取該用戶的所有文章 articles<<(object, ...) -> user.articles << article1 # 來將一篇文章添加到用戶的文章集合中 articles.delete(object, ...) -> user.articles.delete(article1) # 來刪除用戶的某篇文章 articles.destroy(object, ...) -> user.articles.destroy(article1) # 來刪除用戶的某篇文章,同時也會刪除該文章的資料庫記錄 articles=(objects) -> user.articles = [article1, article2] # 來設定用戶的所有文章 article_ids -> user.article_ids # 來獲取用戶的所有文章的 ID article_ids=(ids) -> user.article_ids = [1, 2, 3] # 來設定用戶的所有文章的 ID articles.clear -> articles.clear # 用於從關聯集合中刪除所有 Article 物件,但不會銷毀這些對象。 articles.empty? -> articles.empty? # 用於檢查關聯集合是否為空,如果集合中不包含任何 Article 物件,則返回 true。 articles.size -> articles.size # 用於獲取關聯集合中的 Article 物件數量。 articles.find(...) -> articles.find # 用於查找關聯集合中滿足指定條件的 Article,使用 id 來查找文章。 articles.where(...) -> articles.where # 用於使用條件查找關聯集合中的 Article,並返回一個 ActiveRecord 查詢。 articles.exists?(...) -> articles.exists? # 用於檢查關聯集合中是否存在滿足指定條件的 Article,如果存在則返回 true,否則返回 false。 articles.build(attributes = {}, ...) -> articles.build # 用於建立一個新的 Article,但不會將其保存到資料庫中。可以選擇性地提供屬性(attributes)作為參數,以自定義新建對象的屬性。 articles.create(attributes = {}) -> articles.create # 用於建立一個新的 Article 並將其保存到資料庫中。可以選擇性地提供屬性(attributes)作為參數,以自定義新建對象的屬性。 articles.create!(attributes = {}) -> articles.create! 方法與 articles.create 方法相似,但當保存文章時,如果存在驗 articles.reload -> articles.reload # 用於重新向數據庫發出查詢,以確保關聯集合的數據是最新的。
多對多關聯
在 Rails 中,有兩種方式可以實現多對多關聯: has_many :through 和 has_and_belongs_to_many。
has_many :through
has_many :through 通常在以下情況下使用:
- 需要跟蹤關聯的其他資料:當你需要在多對多關聯中存儲其他資料時,例如時間戳、評分、評論等,
has_many :through是更好的選擇。 - 複雜查詢需求:如果你需要執行複雜的查詢,例如過濾、排序或計數,
has_many :through允許你使用 Active Record 查詢方法對中間模型進行操作,進而實現更高級的查詢。 - 未來擴展性:當你希望你的數據模型具有未來擴展性,以便在以後添加更多的關聯或屬性時,
has_many :through更靈活。
使用 has_many :through:
# User 模型
class User < ApplicationRecord
has_many :user_roles
has_many :roles, through: :user_roles
end
# Role 模型
class Role < ApplicationRecord
has_many :user_roles
has_many :users, through: :user_roles
end
# UserRole 模型作為中間模型
class UserRole < ApplicationRecord
belongs_to :user
belongs_to :role
end
has_and_belongs_to_many
has_and_belongs_to_many 關聯是一種簡單的多對多關聯,
不使用中間模型,只需一個連接表(join table)來維護關聯,通常在以下情況下使用:
- 簡單多對多關聯:當多對多關聯相對簡單,不需要額外的資料(例如,評論或時間戳)時,
has_and_belongs_to_many可以更容易地設置和使用。 - 不需要進行複雜的查詢:如果你只需要基本的查詢(例如,獲取所有關聯記錄),並且不需要進行複雜的過濾或排序,那麼
has_and_belongs_to_many可能是更簡單的選擇。
使用 has_and_belongs_to_many:
# User 模型
class User < ApplicationRecord
has_and_belongs_to_many :roles
end
# Role 模型
class Role < ApplicationRecord
has_and_belongs_to_many :users
end
簡單來說,如果需要將關聯模型視為獨立的實體來操作, 那麼應該設置一個
has_many :through關聯。 如果不需要對關聯模型進行任何操作, 也許設置一個has_and_belongs_to_many關聯會更簡單(需要記住在資料庫中創建連接表 join table)。 如果你需要在關聯模型上進行驗證、回調或使用額外的屬性,應該使用has_many :through。
我們今天先到這!下篇繼續提及 Active Record Associations 的相關概念!
參考資料
為你自己學 Ruby on Rails - Model Active Record Associations PJCHENder - [Rails] Active Record Association (Model)