Rails has_and_belongs_to_many migration


120

İki model var restaurantve userben bir has_and_belongs_to_many ilişkiyi gerçekleştirmek istediğiniz.

Model dosyalarına zaten girdim ve has_and_belongs_to_many :restaurantsvehas_and_belongs_to_many :users

Bu noktada Rails 3 ile benzer bir şey yapabileceğimi varsayıyorum:

rails generate migration ....

ama denediğim her şey başarısız görünüyor. Eminim bu gerçekten basit bir şeydir, raylarda yeniyim, bu yüzden hala öğreniyorum.

Yanıtlar:


260

Alfabetik sırayla yalnızca a restaurant_idve user_id(birincil anahtarsız) ayrı bir birleştirme tablosu eklemeniz gerekir .

Öncelikle taşıma işlemlerinizi çalıştırın, ardından oluşturulan taşıma dosyasını düzenleyin.

Raylar 3

rails g migration create_restaurants_users_table

Raylar 4 :

rails g migration create_restaurants_users

Raylar 5

rails g migration CreateJoinTableRestaurantUser restaurants users

Gönderen docs :

JoinTable ismin bir parçasıysa birleştirme tabloları oluşturacak bir jeneratör de vardır:


Taşıma dosyanız (unutmayın :id => false; birincil anahtarın oluşturulmasını engelleyen şey budur):

Raylar 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

Raylar 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_tootomatik olarak gerekli dizinleri oluşturacaktır. def changeileri veya geri geçişi otomatik olarak algılar, yukarı / aşağıya gerek yoktur.

Raylar 5

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

Not: Ayrıca create_join_table çağrısına parametre olarak aktarılabilen özel bir tablo adı seçeneği de vardır table_name. Gönderen docs

Varsayılan olarak, birleştirme tablosunun adı, create_join_table için sağlanan ilk iki bağımsız değişkenin alfabetik sırayla birleşiminden gelir. Tablonun adını özelleştirmek için bir: tablo_adı seçeneği sağlayın:


8
@Dex - Merak ettiğim için, ters sütun sırasına göre tanımlanan ikinci bir bileşik indeksi neden kullandığınızı açıklayabilir misiniz? Sütun sırasının önemli olmadığı izlenimine kapıldım. Ben DBA değilim, sadece kendi anlayışımı geliştirmek istiyorum. Teşekkürler!
Plainjimbo

11
@Jimbo Bu şekilde ihtiyacınız yok, gerçekten sorularınıza bağlı. Dizinler soldan sağa doğru okunur, böylece arama yapıyorsanız ilki en hızlı olacaktır restaurant_id. İkincisi, araştırıyorsanız yardımcı olacaktır user_id. Her ikisinde de arama yapıyorsanız, veritabanının yalnızca birine ihtiyaç duyacak kadar akıllı olacağını düşünüyorum. Sanırım ikincisinin gerçekten birleştirilmesine gerek yok. Bu daha çok bir örnekti. Yine de bu bir Rails sorusuydu, bu yüzden DB bölümünde yayınlamak daha eksiksiz bir cevap verecektir.
Dex

3
İkinci dizinin bir miktar fazlalığı vardır - hem restaurant_id hem de user_id üzerinde sorguladığınız sürece, SQL'inizdeki sıraları önemli değildir ve ilk dizin kullanılacaktır. Ayrıca, sadece restaurant_id ile ilgili sorgulama yapıyorsanız da kullanılacaktır. İkinci dizinin yalnızca: user_id üzerinde olması gerekir ve yalnızca user_id ile ilgili sorgulama yaptığınız durumlarda kullanılır (anahtarlarının sırası nedeniyle ilk dizinin yardımcı olamayacağı).
Yardboy

13
Raylar 4'te göç olmalıdır rails g migration create_restaurants_usersolmadan masaya sonunda.
Fa11enAngel

6
Rails g migration CreateJoinTableRestaurantUser restaurant kullanıcısı da kullanabilirsiniz. Oku guides.rubyonrails.org/migrations.html#creating-a-join-table
eKek0

36

Buradaki cevaplar oldukça eskidir. Rails 4.0.2'den itibaren, geçişleriniz create_join_table.

Geçiş oluşturmak için şunu çalıştırın:

rails g migration CreateJoinTableRestaurantsUsers restaurant user

Bu, aşağıdakileri oluşturacaktır:

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

Bu sütunları dizine eklemek istiyorsanız, ilgili satırların açıklamasını kaldırın ve gitmeniz iyi olur!


2
Genelde dizin satırlarından birinin açıklamasını kaldırır ve unique: trueona eklerim . Bu, yinelenen ilişkilerin oluşturulmasını önleyecektir.
Toby 1 Kenobi

26

Birleştirme tablosunu oluştururken, iki tablonun geçiş adı / sınıfında alfabetik sırayla listelenmesi gerekliliğine dikkat edin . Model isimleriniz benzer ise, örneğin "abc" ve "abb", bu sizi kolayca ısırabilir. Eğer koşarsan

rails g migration create_abc_abb_table

Kişisel ilişkiler olacak değil beklendiği gibi çalışmayabilir. Kullanmalısın

rails g migration create_abb_abc_table

yerine.


1
Hangisi önce gelir? foo veya foo_bar?
B

1
Bir ray konsolunda çalıştı: ["foo_bar", "foo", "foo bar"]. Sort # => ["foo", "foo bar", "foo_bar"] Unix sıralaması aynı şekilde geliyor.
Shadoath

6

HABTM ilişkileri için bir birleştirme tablosu oluşturmanız gerekir. Sadece birleştirme tablosu vardır ve bu tablonun bir id sütunu olmamalıdır. Bu geçişi deneyin.

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

Bu ilişki rayları kılavuz öğreticilerini kontrol etmelisiniz


Bir modele ihtiyacınız olduğunu düşünmüyorum ve bağlantıda bir HABTM ilişkisi için bir modele ihtiyaç duymayla ilgili hiçbir şey görmüyorum.
B

1
Oluşturulan sorguları hızlandırmak için, id alanlarına endeksler ekleyin.
Martin Röbert
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.