Railsのhas_and_belongs_to_manyへの移行


119

2つのモデルがrestaurantありuser、has_and_belongs_to_many関係を実行する必要があります。

私はすでにモデルファイルに入っておりhas_and_belongs_to_many :restaurantshas_and_belongs_to_many :users

この時点で、Rails 3で次のようなことができるはずです。

rails generate migration ....

しかし、私が試みたすべてが失敗したようです。これは私がRailsを初めて使用する本当にシンプルなものであると確信しているので、まだ学習しています。

回答:


260

restaurant_idand のみuser_id(主キーなし)のアルファベット順で別の結合テーブルを追加する必要があります。

最初にマイグレーションを実行してから、生成されたマイグレーションファイルを編集します。

Rails 3

rails g migration create_restaurants_users_table

Rails 4

rails g migration create_restaurants_users

Rails 5

rails g migration CreateJoinTableRestaurantUser restaurants users

ドキュメントから:

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

Rails 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ドキュメントから

デフォルトでは、結合テーブルの名前は、create_join_tableに提供された最初の2つの引数の和集合からアルファベット順に付けられます。テーブルの名前をカスタマイズするには、:table_nameオプションを指定します。


8
@Dex-好奇心から、列の順序を逆にして定義された2番目の複合インデックスを使用している理由を説明できますか?列の順序は関係ないという印象を受けました。私はDBAではありません。自分の理解を深めたいだけです。ありがとう!
plainjimbo 2012

11
@Jimboその方法は必要ありません。クエリによって異なります。インデックスは左から右に読み込まれるため、最初に検索すると最高速になりますrestaurant_id。2番目は、で検索する場合に役立ちますuser_id。両方を検索している場合、データベースは片方だけで十分なほどスマートだと思います。ですから、2つ目は実際に合成する必要はないと思います。これは単なる例ではありません。これはRailsの質問だったので、DBセクションに投稿すると、より完全な回答が得られます。
Dex

3
2番目のインデックスには冗長性があります。restaurant_idとuser_idの両方でクエリを実行している限り、SQLでのそれらの順序は重要ではなく、最初のインデックスが使用されます。また、restaurant_idに対してのみクエリを実行する場合にも使用されます。2番目のインデックスは:user_idのみである必要があり、user_idでのみクエリを実行している場合に使用されます(最初のインデックスはそのキーの順序のために役立ちません)。
ヤードボーイ、2013

13
Rails 4では、移行は最後にテーブルrails g migration create_restaurants_usersなしでなければなりません。
Fa11enAngel 2013

6
rails g migration CreateJoinTableRestaurantUserレストランユーザーを使用することもできます。guides.rubyonrails.org/migrations.html#creating-a-join-table
eKek0

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
通常、インデックス行の1つをコメント解除unique: trueして追加します。これにより、重複した関係が作成されるのを防ぎます。
トビー1ケノービ

26

結合テーブルを作成するときは、2つのテーブルを移行名/クラスでアルファベット順にリストする必要があるという要件に注意してください。これは、モデル名が類似している場合、たとえば「abc」と「abb」のように簡単に噛み付く可能性があります。あなたが走るとしたら

rails g migration create_abc_abb_table

あなたの関係は期待どおりに機能しません。使用する必要があります

rails g migration create_abb_abc_table

代わりに。


1
どちらが先ですか?fooまたはfoo_bar?
Bセブン

1
Railsコンソールで実行:["foo_bar"、 "foo"、 "foo bar"]。sort#=> ["foo"、 "foo bar"、 "foo_bar"] Unixの並べ替えも同じようになります。
Shadoath

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

この関係レールガイドのチュートリアルを確認する必要があります


私はあなたがモデルを必要としているとは思いませんし、HABTM関係のためのモデルが必要であることについてのリンクには何も表示されていません。
Bセブン

1
生成されたクエリを高速化するには、idフィールドにインデックスを追加します。
MartinRöbert15年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.