Rails迁移用于has_and_belongs_to_many关联表的创建

126

如何使用 script/generate migration 命令来创建一个用于 has_and_belongs_to_many 关联的连接表?

本应用程序运行在 Rails 2.3.2 上,但我也安装了 Rails 3.0.3。

7个回答

231

地点:

class Teacher < ActiveRecord::Base
  has_and_belongs_to_many :students
end

并且

class Student < ActiveRecord::Base
  has_and_belongs_to_many :teachers
end

针对Rails 4:

rails generate migration CreateJoinTableStudentTeacher student teacher

针对Rails 3版本:

rails generate migration students_teachers student_id:integer teacher_id:integer

对于Rails < 3

script/generate migration students_teachers student_id:integer teacher_id:integer

(请注意表名按字母顺序列出了两个连接表)
然后对于仅适用于Rails 3及以下版本的情况,您需要编辑生成的迁移,以便不创建id字段。
create_table :students_teachers, :id => false do |t|

16
这是唯一一个真正回答了问题的答复。 - pingu
8
除了不起作用以外,在Rails 3.2中生成的迁移文件是空白的。 - hoffmanc
2
如果您没有指定任何字段,它将生成一个空的迁移文件。如果您希望Rails自动将它们添加到迁移文件中,则必须指定这些字段。 - Andrew
1
你好,我正在尝试使用 rails generate migration CreateJoinTableTeacherStudent teacher student 而不是 rails generate migration CreateJoinTableStudentTeacher student teacher,这两者一样吗?S(tudent) 需要在 T(eacher) 之前吗? - zx1986
1
@zx1986,是的,它需要按照那个顺序。 - dangerousdave
显示剩余3条评论

139
一个 has_and_belongs_to_many 表必须匹配这个格式。我假设要连接的两个模型已经在数据库中: applesoranges
create_table :apples_oranges, :id => false do |t|
  t.references :apple, :null => false
  t.references :orange, :null => false
end

# Adding the index can massively speed up join tables. Don't use the
# unique if you allow duplicates.
add_index(:apples_oranges, [:apple_id, :orange_id], :unique => true)

如果在索引上使用:unique => true,则应该(在rails3中)将:uniq => true传递给has_and_belongs_to_many
更多信息:Rails文档 更新于2010-12-13 我已经更新了内容以删除id和时间戳...基本上,ma11hew28和nunopolonia是正确的:不应该有id,也不应该有时间戳,否则rails将不允许has_and_belongs_to_many正常工作。

6
实际上,连接表应该只有两个引用列,而不应包含id或时间戳列。这里是一个更好的has_and_belongs_to_many migration示例,来自您提供的链接。我正在寻找一种通过script/generate migration命令行完成它的方法... - ma11hew28
好的。ID 有什么实用的场景? - ma11hew28
一个例子是如果关系足够重要,需要一个视图。它还可以通过传递关系ID来加速访问数据库,而不是反复查找它。它还使得数据库故障排除更容易。特别是如果其他列的ID非常高。记住id:12345比id:54321-id:67890更容易。但是,话虽如此,如果表变得非常大,则可能希望通过不为每个关系分配另一个ID来节省空间。 - docwhat
2
我认为多列索引不是解决这个问题的正确方法。它可以用于查询特定苹果相关的橙子,但不能反过来查询。两个单列索引可以允许双向查询,可能会稍微损失一些特定苹果和橙子组合的存在检查效率。 - Joseph Lord
@JosephLord 是正确的。你应该验证你想要优化哪个方向。此外,你必须知道索引会稍微降低插入速度,因此如果数据输入速度是一个限制因素,就要小心。 - Mauricio Moraes
显示剩余2条评论

14

您需要按照字母顺序将两个模型的名称命名为表格名称,并将这两个模型的ID放入表格中。然后在模型中创建彼此之间的关联,连接每个模型。

以下是一个例子:

# in migration
def self.up
  create_table 'categories_products', :id => false do |t|
    t.column :category_id, :integer
    t.column :product_id, :integer
  end
end

# models/product.rb
has_and_belongs_to_many :categories

# models/category.rb
has_and_belongs_to_many :products

但这种方法不够灵活,你应该考虑使用has_many :through。


6

顶级回答展示了一个综合指数,我不认为会用来查找苹果和橙子。

create_table :apples_oranges, :id => false do |t|
  t.references :apple, :null => false
  t.references :orange, :null => false
end

# Adding the index can massively speed up join tables.
# This enforces uniqueness and speeds up apple->oranges lookups.
add_index(:apples_oranges, [:apple_id, :orange_id], :unique => true)
# This speeds up orange->apple lookups
add_index(:apples_oranges, :orange_id)

我发现“The Doctor What”提供的答案非常有用,相关讨论也很有帮助。


5

在 Rails 4 中,你可以简单地使用

create_join_table :table1s, :table2s

就这样。

注意:你必须提供只包含字母数字的 table1 和 table2。


这是一个很好的最新解决方案。请注意,连接表不可作为模型访问,但可以通过已设置在两个连接表上的has_and_belongs_to_many关系来访问。 - Taylored Web Sites

1

我喜欢做:

rails g migration CreateJoinedTable model1:references model2:references。这样我就可以得到一个类似这样的迁移:

class CreateJoinedTable < ActiveRecord::Migration
  def change
    create_table :joined_tables do |t|
      t.references :trip, index: true
      t.references :category, index: true
    end
    add_foreign_key :joined_tables, :trips
    add_foreign_key :joined_tables, :categories
  end
end

我喜欢在这些列上建立索引,因为我经常会使用这些列进行查找。


如果将“add_foreign_key”放在创建表的同一迁移中,它将失败。 - Adib Saad

1
这篇 Rails关联指南 中的HABTM部分很好,但没有详细说明如何创建连接表。
然而,Rails迁移指南 解释了如何创建连接表:

迁移方法 create_join_table 创建一个HABTM(具有多个并属于多个)连接表。 一个典型的用途是:

create_join_table :products, :categories

默认情况下,连接表的名称来自于提供给 create_join_table 的前两个参数的字母顺序的并集。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接