91

我有兩個模型restaurantuser,我想要執行has_and_belongs_to_many關係。Rails has_and_belongs_to_many遷移

我已經去到模型文件,並添加has_and_belongs_to_many :restaurantshas_and_belongs_to_many :users

我在這一點上認爲我應該可以做一些使用Rails 3:

rails generate migration .... 

,但我的一切嘗試似乎失敗。我敢肯定,這是非常簡單的事情,我是新手,所以我仍然在學習。

回答

193

您需要添加一個單獨的連接表,只有restaurant_iduser_id(無主鍵),字母順序

首先運行您的遷移,然後編輯生成的遷移文件。

導軌3

rails g migration create_restaurants_users_table 

導軌4

rails g migration create_restaurants_users 

導軌5

rails g migration CreateJoinTableRestaurantUser restaurants users 

docs

還有這將產生連接表,如果JoinTable 是名稱的一部分發電機:


你遷移文件(注意:id => false;這是什麼阻止了主鍵的創建):

的Rails 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將自動檢測轉發或回滾遷移,無需向上/向下。

Rails的5

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

注:還有一個自定義表名的選項,可以作爲參數傳遞給create_join_table稱爲table_name。從docs

默認情況下,連接表的名字來自提供給create_join_table,按字母順序 的 前兩個參數的結合。要自定義表的名稱,提供:table_name的 選項:

+0

感謝解決方案爲我完美工作。使用這種方法時犯了一個簡單的錯誤,我嘗試了rake db:在rails命令後進行遷移,並且它沒有生成表。必須做一個rake數據庫:回滾然後編輯文件然後做rake數據庫:遷移但現在一切正常。 – dbslone

+6

@Dex - 出於好奇,你能解釋爲什麼你要使用第二個複合索引,用顛倒的列順序來定義?我的印象是列順序並不重要。我不是DBA,只是想進一步理解我自己。謝謝! – plainjimbo

+9

@Jimbo你不需要這樣,它真的取決於你的查詢。索引從左到右讀取,所以如果您在'restaurant_id'上搜索,則第一個索引將會最快。如果你在'user_id'上搜索,第二個會有幫助。如果你同時搜索,我會認爲數據庫會足夠聰明,只需要一個。所以我想第二個並不需要複雜。這僅僅是一個例子。儘管這是一個Rails問題,因此在DB部分發佈會產生更完整的答案。 – Dex

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 

您必須檢查this relationship rails guide tutorials

+0

我不認爲你需要一個模型,並且在鏈接中沒有看到需要HABTM關係模型的任何東西。 –

+1

要加快生成的查詢,請將索引添加到id字段。 –

24

在創建連接表,請特別注意這兩個表需要在轉移名稱/類字母順序上市的要求。如果您的模型名稱相似,例如「abc」和「abb」。如果你運行

rails g migration create_abc_abb_table 

你的關係將不按預期工作。您必須使用

rails g migration create_abb_abc_table 

改爲。

+0

哪個先到? foo或foo_bar? –

+1

@seven我會去任何unix排序告訴你。 – shacker

+0

在Rails控制檯中運行:[「foo_bar」,「foo」,「foo bar」]。排序#=> [「foo」,「foo bar」,「foo_bar」] Unix排序出現相同。 – Shadoath

23

這裏的答案相當過時。從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 

如果你想索引這些列,取消對各條線,你是好去!