iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0

昨天講述了一些 Active Record Model 的概念之後,今天來點 Active Record Associations 關聯性!

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 :throughhas_and_belongs_to_many

has_many :through

has_many :through 通常在以下情況下使用:

  1. 需要跟蹤關聯的其他資料:當你需要在多對多關聯中存儲其他資料時,例如時間戳、評分、評論等,has_many :through 是更好的選擇。
  2. 複雜查詢需求:如果你需要執行複雜的查詢,例如過濾、排序或計數,has_many :through 允許你使用 Active Record 查詢方法對中間模型進行操作,進而實現更高級的查詢。
  3. 未來擴展性:當你希望你的數據模型具有未來擴展性,以便在以後添加更多的關聯或屬性時,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)來維護關聯,通常在以下情況下使用:

  1. 簡單多對多關聯:當多對多關聯相對簡單,不需要額外的資料(例如,評論或時間戳)時,has_and_belongs_to_many 可以更容易地設置和使用。
  2. 不需要進行複雜的查詢:如果你只需要基本的查詢(例如,獲取所有關聯記錄),並且不需要進行複雜的過濾或排序,那麼 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 的相關概念!

參考資料:

文章同步於個人部落格:Viiisit!(歡迎參觀 ୧ʕ•̀ᴥ•́ʔ୨)


上一篇
Day 19 - 理解 Ruby on Rails,ORM 與 Active Record 是什麼?
下一篇
Day 21 - 理解 Ruby on Rails,Active Record Associations(下)
系列文
從零開始,在 coding 路上的 30 個為什麼?不對!是無數個為什麼!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言