Rails memiliki_dan_belongs_untuk_banyak migrasi

120

Saya memiliki dua model restaurantdan usersaya ingin melakukan hubungan has_and_belongs_to_many.

Saya telah masuk ke file model dan menambahkan has_and_belongs_to_many :restaurantsdanhas_and_belongs_to_many :users

Saya berasumsi pada titik ini saya harus bisa melakukan sesuatu seperti dengan Rails 3:

rails generate migration ....

tetapi semua yang telah saya coba tampaknya gagal. Saya yakin ini adalah sesuatu yang sangat sederhana. Saya baru mengenal rel, jadi saya masih belajar.

dbslone.dll
sumber

Jawaban:

260

Anda perlu menambahkan tabel gabungan terpisah hanya dengan a restaurant_iddan user_id(tanpa kunci utama), dalam urutan abjad .

Pertama-tama, jalankan migrasi Anda, lalu edit file migrasi yang dibuat.

Rel 3

rails g migration create_restaurants_users_table

Rel 4 :

rails g migration create_restaurants_users

Rel 5

rails g migration CreateJoinTableRestaurantUser restaurants users

Dari dokumen :

Ada juga generator yang akan menghasilkan tabel gabungan jika JoinTable adalah bagian dari namanya:


File migrasi Anda (perhatikan :id => false; itu yang mencegah pembuatan kunci utama):

Rel 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

Rel 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_tosecara otomatis akan membuat indeks yang diperlukan. def changeakan otomatis mendeteksi migrasi maju atau mundur, tidak perlu naik / turun.

Rel 5

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

Catatan: Ada juga opsi untuk nama tabel khusus yang dapat diteruskan sebagai parameter untuk dipanggil create_join_table table_name. Dari dokumen

Secara default, nama tabel gabungan berasal dari gabungan dua argumen pertama yang disediakan untuk create_join_table, dalam urutan abjad. Untuk menyesuaikan nama tabel, berikan opsi: nama_tabel:

Dex
sumber
8
@ Dex - Hanya ingin tahu, dapatkah Anda menjelaskan mengapa Anda menggunakan indeks gabungan kedua, yang ditentukan dengan urutan kolom terbalik? Saya mendapat kesan bahwa urutan kolom tidak penting. Saya bukan DBA, hanya ingin memajukan pemahaman saya sendiri. Terima kasih!
plainjimbo
11
@Jimbo Anda tidak membutuhkannya seperti itu, itu sangat tergantung pada pertanyaan Anda. Indeks membaca dari kiri ke kanan sehingga yang pertama akan menjadi yang tercepat jika Anda terus menelusuri restaurant_id. Yang kedua akan membantu jika Anda terus mencari user_id. Jika Anda mencari keduanya, menurut saya database akan cukup pintar untuk hanya membutuhkan satu. Jadi saya kira yang kedua tidak perlu digabungkan. Ini lebih dari sekedar contoh. Ini adalah pertanyaan Rails, jadi posting di bagian DB akan menghasilkan jawaban yang lebih lengkap.
Dex
3
Indeks kedua memiliki beberapa redundansi - selama Anda membuat kueri pada restaurant_id dan user_id, urutannya di SQL Anda tidak masalah, dan indeks pertama akan digunakan. Ini juga akan digunakan jika Anda hanya membuat kueri di restaurant_id. Indeks kedua hanya perlu di: user_id, dan akan digunakan dalam kasus di mana Anda hanya membuat kueri di user_id (yang indeks pertama tidak akan membantu karena urutan kuncinya).
Yardboy
13
Dalam rel 4 migrasi harus rails g migration create_restaurants_userstanpa tabel di akhir.
Fa11enAngel
6
Anda juga bisa menggunakan rel g migrasi pengguna restoran CreateJoinTableRestaurantUser. Baca panduan.rubyonrails.org/migrations.html#creating-a-join-table
eKek0
36

Jawabannya di sini cukup kuno. Mulai Rails 4.0.2, migrasi Anda memanfaatkan create_join_table.

Untuk membuat migrasi, jalankan:

rails g migration CreateJoinTableRestaurantsUsers restaurant user

Ini akan menghasilkan yang berikut:

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

Jika Anda ingin mengindeks kolom ini, hapus tanda komentar pada baris yang bersangkutan dan Anda siap melakukannya!

Jan Klimo
sumber
2
Saya biasanya akan menghapus komentar salah satu baris indeks dan menambahkannya unique: true. Ini akan mencegah hubungan duplikat dibuat.
Toby 1 Kenobi
26

Saat membuat tabel gabungan, perhatikan baik-baik persyaratan bahwa kedua tabel harus dicantumkan dalam urutan abjad dalam nama / kelas migrasi. Ini dapat dengan mudah menggigit Anda jika nama model Anda serupa, misalnya "abc" dan "abb". Jika Anda lari

rails g migration create_abc_abb_table

Hubungan Anda tidak akan bekerja seperti yang diharapkan. Anda harus menggunakan

rails g migration create_abb_abc_table

sebagai gantinya.

shacker
sumber
1
Mana yang lebih dulu? foo atau foo_bar?
B Tujuh
1
Dijalankan di konsol rel: ["foo_bar", "foo", "foo bar"]. Sort # => ["foo", "foo bar", "foo_bar"] Jenis Unix muncul sama.
Shadoath
6

Untuk hubungan HABTM, Anda perlu membuat tabel gabungan. Hanya ada tabel gabungan dan tabel itu tidak boleh memiliki kolom id. Coba migrasi ini.

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

Anda harus memeriksa tutorial panduan rel hubungan ini

Mohit Jain
sumber
Saya tidak berpikir Anda membutuhkan model dan saya tidak melihat apa pun di tautan tentang perlunya model untuk hubungan HABTM.
B Tujuh
1
Untuk mempercepat kueri yang dihasilkan, tambahkan indeks ke bidang id.
Martin Röbert