Active Record Associations

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? 检查。

posted @ 2023-04-21 18:01  卓亦苇  阅读(83)  评论(0编辑  收藏  举报