Activerecord/Datamapper - 有一个 child 属于多个 parents

Activerecord/Datamapper - Have one child belong to many parents

对于以下情况,您将如何设置 activerecord/datamapper 关联:

一个用户创建了一个 "bookshelf",里面有很多书(一本书 object 只有一个 isbn,用于查询 api,has_many 评论 objects 与之关联)。假设 Jack 用一本书 object 创建了一个 "bookshelf"。然后,假设 Jill 使用同一本书 object 创建了一个 "bookshelf"(它具有相同的 ID 和相同的评论)。本书 object 目前有以下代码:

class Book < ActiveRecord::Base
  has_many :reviews
end

然后,当您查看一本书的页面时(从 Jack 创建的 "bookshelf" 单击 link 到它)您应该看到 same book object 当你从 Jill 的 "bookshelf" 点击 link 时(例如 "bookshelves" 都有 link 到 /books/23 因为他们有同一本书 object).

我无法通过 has_many 协会解决这个问题,因为每次用户向他们的 "bookshelf." 添加一本书时,我都需要制作一本新书,我无法理解has_and_belongs_to_many关系,那是不是应该用在这里?我无法在 SO 上找到任何类似的问题,因此非常感谢您的帮助。

我正在使用 Rails 4 和 Ruby 2.1.

这是一张我想要完成的图: Drawing

是的,您必须在 Bookshelf 和 Book 之间定义 many-to-many relationship。在Rails中有两种方法可以实现:

选项 1) 使用 has_and_belongs_to_many

See guide

根据官方文档has_and_belongs_to_many协会:

Specifies a many-to-many relationship with another class. This associates two classes via an intermediate join table. Unless the join table is explicitly specified as an option, it is guessed using the lexical order of the class names. So a join between Developer and Project will give the default join table name of “developers_projects” because “D” precedes “P” alphabetically.

因此,您的 类 应该如下所示:

class Bookshelf < ActiveRecord::Base
  has_and_belongs_to_many :books
end

class Book < ActiveRecord::Base
  has_and_belongs_to_many :bookshelves
  has_many :reviews
end

为您的迁移添加一个加入 table 代:

class CreateBooksBookshelvesJoinTable < ActiveRecord::Migration
  def change
    create_table :books_bookshelves, id: false do |t|
      t.belongs_to :book, index: true
      t.belongs_to :bookshelf, index: true
    end
  end
end

这将在您的数据库中创建一个 books_bookshelves table。 table 将没有主键。您的模型将有两个外键 BookBookshelf.

因此,如果您在用户书架的上下文中调用 self.books,您将获得书架中的图书列表。反之亦然,在一本书的上下文中调用 self.bookshelves 将 return 该书所属的一组书架。

这种方法的问题在于,每次将新书添加到书架时,都会在数据库中创建一条新记录。如果您同意,没有比使用 has_and_belongs_to_many association 更简单的选择了。否则,我建议您选择选项 #2。

选项 2) 使用 has_many :through

另一种选择是使用 has_many, :through association (see guide)。您将不得不再定义一个模型来执行此操作,但在某些用例中它可能会派上用场(请参见下面的示例)。

您的 类 应该如下所示:

class Bookshelf < ActiveRecord::Base
  has_many :books, through: :books_bookshelves
  has_many :books_bookshelves
end

class Book < ActiveRecord::Base
  has_many :bookshelves, through: :books_bookshelves
  has_many :books_bookshelves
  has_many :reviews
end

class BooksBookshelf < ActiveRecord::Base
  belongs_to :book
  belongs_to :bookshelf
end

可能使用 has_many :through association 的最好的事情是它允许您将自定义列添加到连接 table(例如添加列 count 以跟踪有多少本书书架上有相同类型的)。​​

迁移看起来与我们在选项 1 中使用的迁移几乎相同,除了我们要在外键上添加唯一约束(请注意添加约束是可选的):

class CreateBooksBookshelvesJoinTable < ActiveRecord::Migration
  def change
    create_table :books_bookshelves, id: false do |t|
      t.belongs_to :book, index: true
      t.belongs_to :bookshelf, index: true
      # add your custom columns here
    end
    add_index :books_bookshelves, [:book_id, :bookshelf_id], unique: true # to make sure you won't create duplicate records
  end
end

通过这种方法,添加新记录会稍微复杂一些,因为您必须确保没有在连接中插入重复记录 table。 (但是,您可以从迁移中删除唯一约束,以实现与使用 has_and_belongs_to_many 时完全相同的行为。)