Active Record Associations

卓亦葦發表於2023-04-21

The Types of Associations

在 Rails 中,可以透過 ActiveRecord 來定義不同型別的關聯關係(Associations),包括以下幾種:

  1. belongs_to:表示該模型 belongs_to 另一個模型,即該模型擁有一個外來鍵(foreign key)指向另一個模型的主鍵(primary key),通常用於表示一對一或多對一的關係。

  2. has_one:表示該模型 has_one 另一個模型,即另一個模型擁有一個外來鍵指向該模型的主鍵,通常用於表示一對一的關係。

  3. has_many:表示該模型 has_many 另一個模型,即另一個模型擁有一個外來鍵指向該模型的主鍵,通常用於表示一對多的關係。

  4. has_many :through:表示透過中間模型建立多對多的關係,通常用於表示多對多的關係。

  5. has_one :through:表示透過中間模型建立一對一或多對一的關係。

  6. has_and_belongs_to_many:表示建立一個簡單的多對多的關係,通常用於表示只有兩個模型之間的多對多關係。

需要注意的是,這些關聯關係需要在模型之間正確定義和設定,才能夠正確地在應用程式中使用,並且需要考慮到關聯關係的型別、方向、外來鍵、中間模型等因素。正確地定義和使用關聯關係,可以方便地查詢和操作相關資料,並且可以避免出現不必要的程式碼和邏輯。

belongs_to

class CreateBooks < ActiveRecord::Migration[7.0]
  def change
    create_table :authors do |t|
      t.string :name
      t.timestamps
    end

    create_table :books do |t|
      t.belongs_to :author
      t.datetime :published_at
      t.timestamps
    end
  end
end

這是一個建立 authorsbooks 兩個資料庫表的遷移檔案。authors 表包含一個 name 欄位和 created_atupdated_at 兩個時間戳欄位,books 表包含一個 author_id 外來鍵欄位來關聯 authors 表,一個 published_at 欄位和 created_atupdated_at 兩個時間戳欄位。

在 Rails 中,遷移檔案用於建立、修改和刪除資料庫表和欄位。這個遷移檔案中的 change 方法定義瞭如何建立 authorsbooks 兩個表。在 create_table 塊中,我們可以宣告表中的欄位和型別,以及其他選項,如外來鍵關聯等。在這個例子中,我們使用 belongs_to 方法來宣告 books 表與 authors 表之間的關聯關係。

當我們執行這個遷移檔案時,Rails 將會執行 change 方法中的程式碼,並在資料庫中建立 authorsbooks 兩個表。同時,Rails 還會自動建立 author_id 外來鍵索引,以確保 books 表中的每個行都關聯到 authors 表中的一個行。

belongs_to不能確保引用一致性,因此根據用例,您可能還需要在引用列上新增資料庫級外來鍵約束,如下所示:

create_table :books do |t|
  t.belongs_to :author, foreign_key: true
  # ...
end

has_one

當使用 has_one 方法時,一個模型將擁有另一個模型的一個例項作為其屬性之一。這通常用於表示一對一關係,其中一個模型記錄與另一個模型的關聯,而另一個模型只有一個與之相關的記錄。

以下是一個使用 has_one 方法的例子,假設有一個 User 模型和一個 Profile 模型,每個使用者只有一個個人資料:

class User < ApplicationRecord
  has_one :profile
end

class Profile < ApplicationRecord
  belongs_to :user
end

在上面的程式碼中,User 模型使用 has_one 方法宣告瞭與 Profile 模型之間的關聯關係,而 Profile 模型使用 belongs_to 方法宣告瞭與 User 模型之間的關聯關係。

在這個例子中,User 模型將獲得一個名為 profile 的屬性,可以使用它來訪問與使用者相關聯的個人資料。例如,可以使用 user.profile 來獲取使用者的個人資料。在 Profile 模型中,user 屬性將被用於訪問與個人資料相關聯的使用者。

值得注意的是,在 has_one 方法中,預設情況下,Rails 將使用 user_id 欄位作為外來鍵來關聯兩個模型,因此在 Profile 模型中需要使用 belongs_to 方法來宣告與 User 模型之間的關聯關係。

has_one :through

has_one :through 是 Rails 中用於宣告一對一關係的方法,它用於表示兩個模型之間透過第三個關聯模型建立的關係。這個方法通常用於表示一個模型與另一個模型之間的一對一關係,其中一個模型可以關聯一個與之關聯的記錄,而每個關聯記錄只能關聯一個與之關聯的記錄。

以下是一個使用 has_one :through 方法的例子,假設有一個 User 模型和一個 Profile 模型,它們之間透過 ProfileLink 模型建立關聯,每個使用者只能擁有一個個人資料:

class User < ApplicationRecord
  has_one :profile_link
  has_one :profile, through: :profile_link
end

class Profile < ApplicationRecord
  has_one :profile_link
  has_one :user, through: :profile_link
end

class ProfileLink < ApplicationRecord
  belongs_to :user
  belongs_to :profile
end

在上面的程式碼中,User 模型和 Profile 模型都使用 has_one :through 方法宣告瞭與 ProfileLink 模型之間的關聯關係,而 ProfileLink 模型使用 belongs_to 方法宣告瞭與 User 模型和 Profile 模型之間的關聯關係。

在這個例子中,User 模型將獲得一個名為 profile 的屬性,可以使用它來訪問與使用者相關聯的個人資料。例如,可以使用 user.profile 來獲取與使用者相關聯的個人資料。在 Profile 模型中,user 屬性將被用於訪問與個人資料相關聯的使用者。

值得注意的是,在 has_one :through 方法中,需要指定透過哪個關聯模型建立一對一關係,例如:has_one :profile, through: :profile_link 表示 User 模型透過 ProfileLink 模型與 Profile 模型建立了一對一關係。

同時,在這個例子中,ProfileLink 模型還可以新增其他屬性,例如 status 表示使用者的個人資料狀態等,以便更好地描述關聯關係。

has_many

has_many 是 Rails 中用於宣告一對多關係的方法,它用於表示一個模型物件擁有多個其他模型物件的集合。這個方法通常用於表示一個模型與另一個模型之間的關聯,其中一個模型可以擁有多個與之關聯的記錄。

以下是一個使用 has_many 方法的例子,假設有一個 User 模型和一個 Post 模型,每個使用者可以擁有多篇文章:

class User < ApplicationRecord
  has_many :posts
end

class Post < ApplicationRecord
  belongs_to :user
end

在上面的程式碼中,User 模型使用 has_many 方法宣告瞭與 Post 模型之間的關聯關係,而 Post 模型使用 belongs_to 方法宣告瞭與 User 模型之間的關聯關係。

在這個例子中,User 模型將獲得一個名為 posts 的屬性,可以使用它來訪問與使用者相關聯的所有文章。例如,可以使用 user.posts 來獲取與使用者相關聯的所有文章。在 Post 模型中,user 屬性將被用於訪問與文章相關聯的使用者。

值得注意的是,在 has_many 方法中,預設情況下,Rails 將使用 user_id 欄位作為外來鍵來關聯兩個模型,因此在 Post 模型中需要使用 belongs_to 方法來宣告與 User 模型之間的關聯關係。

has_many :through

has_many :through 是 Rails 中用於宣告多對多關係的方法,它用於表示兩個模型之間透過第三個關聯模型建立的關係。這個方法通常用於表示一個模型與另一個模型之間的多對多關係,其中一個模型可以關聯多個與之關聯的記錄,而每個關聯記錄都可以關聯多個與之關聯的記錄。

以下是一個使用 has_many :through 方法的例子,假設有一個 User 模型和一個 Group 模型,它們之間透過 Membership 模型建立關聯,每個使用者可以屬於多個組:

class User < ApplicationRecord
  has_many :memberships
  has_many :groups, through: :memberships
end

class Group < ApplicationRecord
  has_many :memberships
  has_many :users, through: :memberships
end

class Membership < ApplicationRecord
  belongs_to :user
  belongs_to :group
end

在上面的程式碼中,User 模型和 Group 模型都使用 has_many :through 方法宣告瞭與 Membership 模型之間的關聯關係,而 Membership 模型使用 belongs_to 方法宣告瞭與 User 模型和 Group 模型之間的關聯關係。

在這個例子中,User 模型將獲得一個名為 groups 的屬性,可以使用它來訪問與使用者相關聯的所有組。例如,可以使用 user.groups 來獲取與使用者相關聯的所有組。在 Group 模型中,users 屬性將被用於訪問所有屬於該組的使用者。

值得注意的是,在 has_many :through 方法中,需要指定透過哪個關聯模型建立多對多關係,例如:has_many :groups, through: :memberships 表示 User 模型透過 Membership 模型與 Group 模型建立了多對多關係。

同時,在這個例子中,Membership 模型還可以新增其他屬性,例如 status 表示使用者在組中的狀態等,以便更好地描述關聯關係。

has_manyhas_many :through 都是 Rails 中用於建立關聯關係的方法,但它們之間有一些區別。

has_many :through和has_many 區別

has_many 建立的是一對多的關聯關係,其中一個模型物件擁有多個其他模型物件的集合。這個方法通常用於表示一個模型與另一個模型之間的關聯,其中一個模型可以擁有多個與之關聯的記錄。例如,一個使用者可以擁有多篇文章。

has_many :through 建立的是多對多的關聯關係,其中兩個模型之間透過第三個關聯模型建立的關係。這個方法通常用於表示一個模型與另一個模型之間的多對多關係,其中一個模型可以關聯多個與之關聯的記錄,而每個關聯記錄都可以關聯多個與之關聯的記錄。例如,一個使用者可以屬於多個組,一個組也可以有多個使用者。

因此,has_many :through 更加靈活,可以用於建立更為複雜的關聯關係。同時,has_many :through 還可以在關聯模型中新增其他屬性,例如關聯記錄的狀態等。

需要注意的是,在使用 has_many :through 方法建立多對多關聯關係時,需要指定透過哪個關聯模型建立多對多關係。而在使用 has_many 建立一對多關聯關係時,則不需要指定。

The has_and_belongs_to_many Association

has_and_belongs_to_many 是 Rails 中用於宣告多對多關係的另一種方法,與 has_many :through 不同的是,它不需要使用第三個關聯模型來建立多對多關係。這個方法通常用於表示兩個模型之間的多對多關係,其中一個模型可以關聯多個與之關聯的記錄,而每個關聯記錄也可以關聯多個與之關聯的記錄。

以下是一個使用 has_and_belongs_to_many 方法的例子,假設有一個 Book 模型和一個 Author 模型,它們之間建立了多對多關係:

class Book < ApplicationRecord
  has_and_belongs_to_many :authors
end

class Author < ApplicationRecord
  has_and_belongs_to_many :books
end

在上面的程式碼中,Book 模型和 Author 模型都使用 has_and_belongs_to_many 方法宣告瞭彼此之間的多對多關係。

在這個例子中,Book 模型將獲得一個名為 authors 的屬性,可以使用它來訪問與圖書相關聯的所有作者。例如,可以使用 book.authors 來獲取與書籍相關聯的作者列表。在 Author 模型中,books 屬性將被用於訪問與作者相關聯的所有書籍。

需要注意的是,在使用 has_and_belongs_to_many 方法建立多對多關聯關係時,需要在資料庫中建立一箇中間表來儲存關聯關係。這個中間表的名稱應該是兩個模型名稱的複數形式的字母排序後的連線,例如,在上面的例子中,中間表的名稱應該是 authors_books

同時,使用 has_and_belongs_to_many 方法建立多對多關聯關係時,不能在中間表中新增其他屬性,因為它只是用於儲存兩個模型之間的關聯關係。如果需要在關聯關係中新增其他屬性,應該使用 has_many :through 方法來建立多對多關係。

Choosing Between belongs_to and has_one

在 Rails 中,belongs_tohas_one 是兩種用於定義兩個模型之間的一對一關係的方法。選擇它們之間的方法取決於兩個模型之間關係的性質。

當外來鍵儲存在宣告關聯的模型的表中時,使用 belongs_to。例如,考慮一個 Car 模型,它屬於一個 Manufacturer

class Car < ApplicationRecord
  belongs_to :manufacturer
end

class Manufacturer < ApplicationRecord
  has_many :cars
end

在這個例子中,cars 表有一個外來鍵 manufacturer_id 引用 manufacturers 表。由於外來鍵儲存在 cars 表中,我們在 Car 模型中使用 belongs_to 來定義關聯。

另一方面,當外來鍵儲存在關聯模型的表中時,使用 has_one。例如,考慮一個 Person 模型,它有一個 Address

class Person < ApplicationRecord
  has_one :address
end

class Address < ApplicationRecord
  belongs_to :person
end

在這個例子中,addresses 表有一個外來鍵 person_id 引用 people 表。由於外來鍵儲存在 addresses 表中,我們在 Person 模型中使用 has_one 來定義關聯。

一般來說,選擇 belongs_to 還是 has_one 取決於外來鍵儲存的位置。如果它儲存在宣告關聯的模型的表中,使用 belongs_to。如果它儲存在關聯模型的表中,使用 has_one

Choosing Between has_many :through and has_and_belongs_to_many

在 Rails 中,has_many :throughhas_and_belongs_to_many 是兩種用於定義多對多關係的方法。選擇它們之間的方法取決於你是否需要在關聯關係中儲存其他屬性。

has_many :through 允許你使用中間模型來連線兩個模型,並且可以在中間模型中儲存其他屬性。這使得 has_many :through 更加靈活,適用於需要在關聯關係中儲存更多資訊的情況。例如,考慮一個 Patient 模型和一個 Doctor 模型,它們之間需要建立多對多關係,而每個關聯關係還需要儲存一個 appointment_date 屬性:

class Patient < ApplicationRecord
  has_many :appointments
  has_many :doctors, through: :appointments
end

class Doctor < ApplicationRecord
  has_many :appointments
  has_many :patients, through: :appointments
end

class Appointment < ApplicationRecord
  belongs_to :patient
  belongs_to :doctor
end

在上面的例子中,Patient 模型和 Doctor 模型之間的多對多關係透過 Appointment 模型建立。Appointment 模型中儲存了 appointment_date 屬性,表示預約時間。可以使用以下程式碼來訪問與患者相關聯的所有醫生:

patient.doctors

has_and_belongs_to_many 允許你在兩個模型之間建立簡單的多對多關係,但不能在中間表中儲存其他屬性。因此,如果你不需要在關聯關係中儲存其他屬性,可以使用 has_and_belongs_to_many。例如,考慮一個 Student 模型和一個 Course 模型,它們之間需要建立多對多關係:

class Student < ApplicationRecord
  has_and_belongs_to_many :courses
end

class Course < ApplicationRecord
  has_and_belongs_to_many :students
end

在上面的例子中,Student 模型和 Course 模型之間的多對多關係可以直接透過中間表建立,而不需要使用中間模型。

總之,如果你需要在關聯關係中儲存其他屬性,應該使用 has_many :through。如果不需要儲存其他屬性,則可以使用 has_and_belongs_to_many 來建立多對多關係。

Polymorphic Associations

在 Rails 中,多型關聯(Polymorphic Associations)允許一個模型屬於多個不同型別的其他模型,同時這些其他模型也可以有多個關聯的模型。這種關聯通常用於需要共享相同行為或屬性的模型之間。

例如,考慮一個 Comment 模型,它可以屬於多個其他模型(例如 PostPhoto),同時這些其他模型也可以有多個評論:

class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
end

class Post < ApplicationRecord
  has_many :comments, as: :commentable
end

class Photo < ApplicationRecord
  has_many :comments, as: :commentable
end

在上面的例子中,Comment 模型使用 belongs_to 方法宣告瞭多型關聯。commentable 是一個多型關聯欄位,它可以屬於任何其他模型。在 PostPhoto 模型中,使用 has_many 方法宣告瞭多型關聯關係,並使用 as 選項指定了多型關聯欄位的名稱。

使用多型關聯時,需要在資料庫中建立一個 comments 表。這個表需要包含一個 commentable_type 欄位和一個 commentable_id 欄位,用於儲存關聯的模型。可以使用以下程式碼建立 comments 表:

rails generate migration CreateComments commentable:references{polymorphic}:index body:text

上面的程式碼將生成一個名為 CreateComments 的遷移檔案,該檔案將建立一個 comments 表,並新增一個 commentable_type 欄位和一個 commentable_id 欄位,同時還新增了一個 body 欄位用於儲存評論內容。

可以使用以下程式碼來訪問與 Post 相關聯的所有評論:

post.comments

可以使用以下程式碼來訪問與 Photo 相關聯的所有評論:

photo.comments

總之,多型關聯允許一個模型屬於多個不同型別的其他模型,並且這些其他模型也可以有多個關聯的模型。這種關聯通常用於需要共享相同行為或屬性的模型之間。

Self Joins

Self Joins 是指在一個表中,透過外來鍵關聯自身的另一行資料。Self Joins 常用於需要建立層次結構的資料模型,例如組織結構、分類等。

在 Rails 中,可以透過在模型中使用 belongs_tohas_many 方法來實現 Self Joins。具體實現方式是,在模型中定義一個外來鍵欄位來引用自身的 ID,然後透過 belongs_to 方法宣告自身與父級的關聯,再透過 has_many 方法宣告自身與子級的關聯。

下面是一個簡單的例子,假設有一個 Category 模型,每個分類可以有多個子分類,同時也可以屬於一個父分類:

class Category < ApplicationRecord
  belongs_to :parent, class_name: 'Category', optional: true
  has_many :children, class_name: 'Category', foreign_key: 'parent_id'
end

上面的程式碼中,Category 模型透過 belongs_to 方法宣告與父級的關聯,使用 class_name 選項指定關聯的模型名稱為 Category,同時使用 optional: true 選項表示父級可以為空。透過 has_many 方法宣告與子級的關聯,使用 class_name 選項指定關聯的模型名稱為 Category,使用 foreign_key 選項指定外來鍵欄位為 parent_id

在資料庫中,需要建立一個 categories 表來儲存分類。該表需要包含一個 parent_id 欄位用於儲存父級分類的 ID,可以使用以下程式碼建立 categories 表:

rails generate migration CreateCategories name:string parent:references

上面的程式碼將生成一個名為 CreateCategories 的遷移檔案,該檔案將建立一個 categories 表,並新增一個 name 欄位用於儲存分類名稱,以及一個 parent_id 欄位用於儲存父級分類的ID。

可以使用以下程式碼來訪問一個分類的父級:

category.parent

可以使用以下程式碼來訪問一個分類的子級:

category.children

總之,Self Joins 允許在一個表中透過外來鍵關聯自身的另一行資料,常用於需要建立層次結構的資料模型。在 Rails 中,可以透過在模型中使用 belongs_tohas_many 方法來實現 Self Joins,透過定義一個外來鍵欄位來引用自身的 ID,然後宣告自身與父級的關聯和自身與子級的關聯。

Tips, Tricks, and Warnings

Controlling Caching

# retrieves books from the database
author.books.load

# uses the cached copy of books
author.books.size

# uses the cached copy of books
author.books.empty?

這是關於 ActiveRecord 的程式碼示例,它展示瞭如何使用快取來訪問一個作者(author)的書籍(books)。

第一行程式碼 author.books.load 從資料庫中檢索作者的書籍,並將其儲存在快取中。這意味著在下一行程式碼和之後的程式碼中,將使用快取中的書籍,而不是從資料庫中再次檢索它們。

第二行程式碼 author.books.size 返回快取中作者的書籍數量,而不是從資料庫中再次檢索它們。這是因為在第一行程式碼中,author.books.load 將書籍儲存在快取中,因此在下一行程式碼中,可以直接從快取中獲取書籍數量,而不需要從資料庫中再次檢索它們。

第三行程式碼 author.books.empty? 返回一個布林值,指示快取中作者的書籍是否為空。同樣地,這是因為在第一行程式碼中,author.books.load 將書籍儲存在快取中,因此在第三行程式碼中,可以直接從快取中檢查書籍是否為空,而不需要從資料庫中再次檢索它們。

這種使用快取的方式可以幫助提高應用程式的效能,因為它避免了在每次訪問物件時都需要從資料庫中檢索資料的開銷。但是,需要注意的是,如果在快取中的資料與資料庫中的資料不同步,則可能會導致資料不一致。因此,在使用快取時,需要仔細考慮如何更新快取以確保資料的正確性。

Creating Foreign Keys for belongs_to Associations

在關係型資料庫中,外來鍵(Foreign Key)是一種用於建立表之間關聯的技術。當一個表中的列引用另一個表的主鍵時,就會建立一個外來鍵。在 Rails 中,透過使用 belongs_to 關聯,可以輕鬆地建立外來鍵。以下是一個實際的示例:

假設您正在構建一個部落格應用程式,其中包含多個文章(Post)和多個評論(Comment)。每個評論都屬於一個特定的文章,因此您需要在評論表中建立一個外來鍵,以引用文章表中的主鍵。

首先,您需要在 Comment 模型中新增一個 belongs_to 關聯:

class Comment < ApplicationRecord
  belongs_to :post
end

然後,您需要在評論表中新增一個名為 post_id 的整數列,用於儲存文章的主鍵值。在 Rails 中,可以使用資料庫遷移來新增此列:

rails generate migration AddPostIdToComments post:references

這將生成一個包含 add_reference 方法的遷移檔案,該方法將在評論表中新增一個 post_id 列,並將其設定為引用文章表的主鍵。

最後,您需要執行遷移,以將更改應用於資料庫:

rake db:migrate

現在,當您建立一個新評論時,Rails 將自動在評論表中設定正確的 post_id 值,以引用相應的文章。

例如,您可以透過以下程式碼將一條評論關聯到一篇文章:

post = Post.first
post.comments.create(body: "Great post!")

這是一個示例程式碼,用於在 Rails 應用程式中建立新評論並將其與特定文章關聯。

首先,Post.first 獲取文章表中的第一篇文章,並將其分配給變數 post。然後,post.comments.create 用於建立一個新評論,並將其與 post 變數中儲存的文章關聯起來。這是透過 has_many :comments 關聯和 Comment 模型中的 belongs_to :post 關聯實現的。

具體來說,post.comments 返回一個關聯物件,該物件允許您訪問與特定文章相關聯的所有評論。然後,create 方法用於建立一個新評論,並將其與 post 關聯起來。在本例中,新評論的 body 屬性設定為 "Great post!"

最後,新評論將被儲存到資料庫中,並且在 comments 表中將包含一個新行,其中包含評論的內容和與之相關聯的文章的主鍵值。

這是一個典型的 Rails 應用程式中的程式碼示例,用於演示如何使用關聯模型和建立新物件。

Creating Join Tables for has_and_belongs_to_many Associations

在 Rails 中,has_and_belongs_to_many(HABTM)關聯用於建立多對多的關係。在關係型資料庫中,通常需要使用一箇中間表來儲存這種關聯,這個中間表被稱為“聯接表”(Join Table)。

建立聯接表的步驟如下:

  1. 建立一個名為 table1_table2 的表,其中 table1table2 分別是要關聯的兩個表的名稱。例如,如果您想要關聯 usersgroups 表,則可以建立一個名為 users_groups 的聯接表。

  2. 新增兩個整數列,分別用於儲存關聯表的主鍵。這些列通常被命名為 table1_idtable2_id,例如 user_idgroup_id

  3. table1table2 中的模型檔案中新增 has_and_belongs_to_many 關聯。例如,在 User 模型中,您可以這樣新增一個 has_and_belongs_to_many 關聯:

class User < ApplicationRecord
  has_and_belongs_to_many :groups
end

這將指示 Rails 透過 users_groups 表將 usersgroups 表關聯起來。

以下是一個實際的示例:

假設您正在構建一個社交網路應用程式,其中使用者(User)可以加入多個組(Group),而每個組也可以有多個使用者。為了實現這種多對多關係,您需要建立一個聯接表。

首先,您可以使用以下命令建立一個名為 groups_users 的聯接表:

rails generate migration CreateGroupsUsers

然後,您可以使用以下程式碼向遷移檔案中新增表的定義:

class CreateGroupsUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :groups_users, id: false do |t|
      t.references :group, null: false, foreign_key: true
      t.references :user, null: false, foreign_key: true
    end
  end
end

這將建立一個名為 groups_users 的聯接表,並新增 group_iduser_id 兩個整數列,用於儲存關聯表的主鍵。

最後,您可以向 UserGroup 模型中新增 has_and_belongs_to_many 關聯,以指示它們之間的多對多關係:

class User < ApplicationRecord
  has_and_belongs_to_many :groups
end

class Group < ApplicationRecord
  has_and_belongs_to_many :users
end

現在,您可以使用 << 運算子向使用者新增組,例如:

user = User.first
group = Group.first
user.groups << group

這將將 usergroup 關聯起來,並在 groups_users 表中新增一個新行,其中包含 user_idgroup_id 的值。

透過使用 has_and_belongs_to_many 關聯和聯接表,您可以輕鬆地建立多對多關係,並在 Rails 應用程式中儲存和檢索相關資料。

Controlling Association Scope

在 Rails 中,可以使用 scope 方法來控制關聯模型的查詢範圍。這可以幫助您過濾不必要的資料,以提高應用程式的效能和可維護性。

例如,假設您正在構建一個電子商務應用程式,其中訂單(Order)有多個訂單項(OrderItem),並且每個訂單項都屬於一個特定的產品(Product)。現在,您想要檢索某個產品的所有訂單項。

首先,您可以在 Product 模型中新增一個 has_many 關聯,以指示每個產品都有多個訂單項:

class Product < ApplicationRecord
  has_many :order_items
end

然後,您可以使用 scope 方法來指定只檢索與特定產品相關聯的訂單項:

class OrderItem < ApplicationRecord
  belongs_to :product
  scope :for_product, -> (product) { where(product_id: product.id) }
end

這將建立一個名為 for_product 的作用域,它接受一個產品物件作為引數,並返回與該產品相關聯的所有訂單項。

現在,您可以在控制器或檢視中使用 for_product 作用域來檢索與特定產品相關聯的所有訂單項。例如,假設您正在顯示某個產品的詳細資訊,並想要列出所有相關的訂單項:

def show
  @product = Product.find(params[:id])
  @order_items = OrderItem.for_product(@product)
end

這將檢索與 @product 相關聯的所有訂單項,並將它們分配給 @order_items 變數。由於使用了作用域,查詢將僅返回與特定產品相關聯的訂單項,而不是所有訂單項,從而提高了查詢的效能和可維護性。

透過使用作用域方法,您可以輕鬆地控制關聯模型的查詢範圍,並過濾不必要的資料,以提高應用程式的效能和可維護性。

Bi-directional Associations

在 Rails 中,雙向關聯(Bi-directional Associations)是指兩個關聯模型之間的相互關係,其中每個模型都可以訪問另一個模型。這可以透過在兩個模型中都定義關聯來實現。

例如,假設您正在構建一個部落格應用程式,其中文章(Post)可以有多個標籤(Tag),而每個標籤也可以與多篇文章相關聯。為了實現這種雙向關聯,您可以在 PostTag 模型中都定義一個關聯。

首先,您可以在 Post 模型中新增一個 has_and_belongs_to_many 關聯,以指示每篇文章都可以有多個標籤:

class Post < ApplicationRecord
  has_and_belongs_to_many :tags
end

然後,您可以在 Tag 模型中新增一個相反的 has_and_belongs_to_many 關聯,以指示每個標籤也可以與多篇文章相關聯:

class Tag < ApplicationRecord
  has_and_belongs_to_many :posts
end

現在,您可以在控制器或檢視中使用這些關聯來訪問相互關聯的模型。例如,假設您想要列出所有帶有特定標籤的文章:

def index
  @tag = Tag.find(params[:tag_id])
  @posts = @tag.posts
end

這將檢索與 @tag 相關聯的所有文章,並將它們分配給 @posts 變數。由於使用了雙向關聯,您可以透過 @tag.posts 訪問所有相關的文章,也可以透過 @post.tags 訪問所有相關的標籤。

雙向關聯使得在兩個關聯模型之間進行導航變得非常容易。透過在每個模型中都定義關聯,您可以輕鬆地訪問相互關聯的資料,並簡化程式碼的編寫和維護。

Detailed Association Reference

belongs_to Association Reference

這些方法都是 ActiveRecord 中用於操作關聯關係的方法,主要用於設定、建立、檢索和重新載入關聯物件。下面是這些方法的解釋:

  • association:獲取關聯物件。例如,如果 Book 模型與 Author 模型存在 belongs_to 關聯關係,則可以使用 book.author 獲取與該書籍關聯的作者物件。

  • association=(associate):設定關聯物件。例如,如果 Book 模型與 Author 模型存在 belongs_to 關聯關係,則可以使用 book.author = authorbook 物件與特定的 author 物件關聯起來。

  • build_association(attributes = {}):建立一個新的關聯物件,並將其與當前物件關聯起來。例如,如果 Book 模型與 Author 模型存在 has_one 關聯關係,則可以使用 book.build_author(author_name: "John Doe") 建立一個新的 Author 物件,並將其與 book 物件關聯起來。

  • create_association(attributes = {}):建立一個新的關聯物件,並將其與當前物件關聯起來,然後將其儲存到資料庫中。例如,如果 Book 模型與 Author 模型存在 has_many 關聯關係,則可以使用 book.authors.create(author_name: "John Doe") 建立一個新的 Author 物件,並將其與 book 物件關聯起來,然後將其儲存到資料庫中。

  • create_association!(attributes = {}):與 create_association 方法類似,但是如果建立失敗(例如,因為驗證失敗),則會引發異常。

  • reload_association:重新載入關聯物件,並將其與資料庫中的最新資料同步。例如,如果您對關聯物件進行了更改,並希望檢索最新版本,則可以使用 book.author.reload_association 重新載入與該書籍關聯的作者物件。

  • association_changed?:檢查關聯物件是否已更改。例如,如果您更改了與 book 物件關聯的 author 物件,則可以使用 book.author_changed? 檢查是否更改了該物件。

  • association_previously_changed?:檢查關聯物件在上一次儲存時是否已更改。例如,如果您想知道與 book 物件關聯的 author 物件在上一次儲存時是否已更改,則可以使用 book.author_previously_changed? 檢查。

相關文章