Rails has_and_belongs_to_many迁移


119

我有两个型号restaurantuser我要执行has_and_belongs_to_many关系。

我已经进入模型文件并添加了has_and_belongs_to_many :restaurantshas_and_belongs_to_many :users

我认为此时我应该能够对Rails 3进行类似的操作:

rails generate migration ....

但是我尝试过的一切似乎都失败了。我确信这是非常简单的事情,我是Rails的新手,所以我还在学习。

Answers:


259

您需要添加一个单独的联接表,该联接表仅按字母顺序排列一个restaurant_iduser_id(无主键)

首先运行您的迁移,然后编辑生成的迁移文件。

导轨3

rails g migration create_restaurants_users_table

Rails 4

rails g migration create_restaurants_users

滑轨5

rails g migration CreateJoinTableRestaurantUser restaurants users

文档

如果JoinTable是名称的一部分,则还有一个生成器将生成联接表:


您的迁移文件(请注意:id => false;它是阻止创建主键的原因):

导轨3

class CreateRestaurantsUsers < ActiveRecord::Migration
  def self.up
    create_table :restaurants_users, :id => false do |t|
        t.references :restaurant
        t.references :user
    end
    add_index :restaurants_users, [:restaurant_id, :user_id]
    add_index :restaurants_users, :user_id
  end

  def self.down
    drop_table :restaurants_users
  end
end

滑轨4

class CreateRestaurantsUsers < ActiveRecord::Migration
  def change
    create_table :restaurants_users, id: false do |t|
      t.belongs_to :restaurant
      t.belongs_to :user
    end
  end
end

t.belongs_to将自动创建必要的索引。def change将自动检测向前或向后迁移,而无需进行上/下迁移。

滑轨5

create_join_table :restaurants, :users do |t|
  t.index [:restaurant_id, :user_id]
end

注意:还有一个自定义表名称的选项,可以作为参数传递给名为的create_join_table table_name。来自文档

默认情况下,联接表的名称来自提供给create_join_table的前两个参数的并集(按字母顺序)。要定制表的名称,请提供:table_name选项:


7
@Dex-出于好奇,您能否解释一下为什么要使用第二个复合索引(以相反的列顺序定义)?我的印象是列顺序无关紧要。我不是DBA,只是想进一步了解自己。谢谢!
plainjimbo 2012年

10
@Jimbo您不需要这种方式,它实际上取决于您的查询。索引从左到右读取,因此如果在上搜索,第一个索引最快restaurant_id。如果您正在搜索,第二个会有所帮助user_id。如果您同时在两者上进行搜索,我会认为该数据库足够聪明,只需要一个即可。因此,我猜想第二个并不需要很复杂。这仅仅是一个例子。不过,这是一个Rails问题,因此在DB部分中发布将产生更完整的答案。
Dex 2012年

3
第二个索引具有一定的冗余性-只要您同时在restaurant_id和user_id上查询,它们在SQL中的顺序无关紧要,并且将使用第一个索引。如果仅在restaurant_id上查询,也将使用它。第二个索引仅需位于:user_id上,并且仅在查询user_id时才会使用(由于其键的顺序,第一个索引无济于事)。
Yardboy

13
在导轨4中,迁移必须最后rails g migration create_restaurants_users没有桌子
Fa11enAngel 2013年

6
您也可以使用rails g迁移CreateJoinTableRestaurantUser餐厅用户。阅读guides.rubyonrails.org/migrations.html#creating-a-join-table
eKek0 2013年

36

这里的答案已经过时了。从Rails 4.0.2开始,您的迁移利用create_join_table

要创建迁移,请运行:

rails g migration CreateJoinTableRestaurantsUsers restaurant user

这将生成以下内容:

class CreateJoinTableRestaurantsUsers < ActiveRecord::Migration
  def change
    create_join_table :restaurants, :users do |t|
      # t.index [:restaurant_id, :user_id]
      # t.index [:user_id, :restaurant_id]
    end
  end
end

如果要为这些列建立索引,请取消注释相应的行,然后开始!


2
我通常会取消注释其中之一的索引行并将其添加unique: true。这将防止创建重复的关系。
Toby 1 Kenobi

26

创建联接表时,请特别注意两个表必须在迁移名称/类中按字母顺序列出的要求。如果您的型号名称相似(例如“ abc”和“ abb”),这很容易咬人。如果你要跑步

rails g migration create_abc_abb_table

您的关系将无法正常工作。您必须使用

rails g migration create_abb_abc_table

代替。


1
哪个先到?foo或foo_bar?
B 2015年

1
在rails控制台中运行:[“ foo_bar”,“ foo”,“ foo bar”]。sort#=> [“ foo”,“ foo bar”,“ foo_bar”] Unix排序也一样。
阴影

6

对于HABTM关系,您需要创建一个联接表。只有联接表,并且该表不应具有id列。尝试此迁移。

def self.up
  create_table :restaurants_users, :id => false do |t|
    t.integer :restaurant_id
    t.integer :user_id
  end
end

def self.down
  drop_table :restaurants_users
end

您必须检查此关系Rails指南教程


我认为您不需要模型,并且链接中没有任何关于需要HABTM关系模型的信息。
B 2015年

1
要加快生成的查询的速度,请向id字段添加索引。
MartinRöbert'15
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.