Rails has_and_belongs_to_many migration


120

Ho due modelli restaurante userdesidero eseguire una relazione has_and_belongs_to_many.

Sono già entrato nei file del modello e ho aggiunto i file has_and_belongs_to_many :restaurantsehas_and_belongs_to_many :users

Presumo a questo punto che dovrei essere in grado di fare qualcosa di simile con Rails 3:

rails generate migration ....

ma tutto quello che ho provato sembra fallire. Sono sicuro che questo sia qualcosa di veramente semplice, sono nuovo su rails, quindi sto ancora imparando.

Risposte:


260

È necessario aggiungere una tabella di join separata con solo un restaurant_ide user_id(nessuna chiave primaria), in ordine alfabetico .

Esegui prima le migrazioni, quindi modifica il file di migrazione generato.

Rotaie 3

rails g migration create_restaurants_users_table

Rotaie 4 :

rails g migration create_restaurants_users

Rotaie 5

rails g migration CreateJoinTableRestaurantUser restaurants users

Dai documenti :

C'è anche un generatore che produrrà tabelle di join se JoinTable fa parte del nome:


Il tuo file di migrazione (nota il :id => false; è ciò che impedisce la creazione di una chiave primaria):

Rotaie 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

Rotaie 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_tocreerà automaticamente gli indici necessari. def changerileverà automaticamente una migrazione in avanti o rollback, senza bisogno di up / down.

Rotaie 5

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

Nota: esiste anche un'opzione per un nome di tabella personalizzato che può essere passato come parametro a create_join_table chiamato table_name. Dai documenti

Per impostazione predefinita, il nome della tabella di join deriva dall'unione dei primi due argomenti forniti a create_join_table, in ordine alfabetico. Per personalizzare il nome della tabella, fornire un'opzione: nome_tabella:


8
@Dex - Solo per curiosità, potresti spiegare perché stai usando un secondo indice composto, definito con l'ordine delle colonne invertito? Avevo l'impressione che l'ordine delle colonne non avesse importanza. Non sono un DBA, voglio solo approfondire la mia comprensione. Grazie!
plainjimbo

11
@Jimbo Non ne hai bisogno in questo modo, dipende davvero dalle tue domande. Gli indici leggono da sinistra a destra, quindi il primo sarà il più veloce se stai cercando restaurant_id. Il secondo ti aiuterà se stai cercando user_id. Se stai cercando su entrambi, penso che il database sarebbe abbastanza intelligente da averne bisogno solo uno. Quindi immagino che il secondo non abbia davvero bisogno di essere composto. Questo era più solo un esempio. Questa era una domanda su Rails, quindi pubblicare nella sezione DB avrebbe prodotto una risposta più completa.
Dex

3
Il secondo indice ha una certa ridondanza: fintanto che esegui query sia su restaurant_id che su user_id, il loro ordine nel tuo SQL non ha importanza e verrà utilizzato il primo indice. Verrà utilizzato anche se stai solo interrogando restaurant_id. Il secondo indice deve essere solo su: user_id e verrebbe utilizzato nei casi in cui si esegue una query solo su user_id (cosa che il primo indice non aiuterebbe a causa dell'ordine delle sue chiavi).
Yardboy

13
In rails 4 la migrazione deve essere rails g migration create_restaurants_userssenza tabella alla fine.
Fa11enAngel

6
Puoi anche usare rails g migration CreateJoinTableRestaurantUser utente del ristorante. Leggi guides.rubyonrails.org/migrations.html#creating-a-join-table
eKek0

36

Le risposte qui sono piuttosto datate. A partire da Rails 4.0.2, le tue migrazioni fanno uso dicreate_join_table .

Per creare la migrazione, esegui:

rails g migration CreateJoinTableRestaurantsUsers restaurant user

Questo genererà quanto segue:

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

Se vuoi indicizzare queste colonne, rimuovi il commento dalle rispettive righe e sei a posto!


2
Generalmente rimuovo il commento da una delle righe dell'indice e aggiungo unique: truead essa. Ciò impedirà la creazione di relazioni duplicate.
Toby 1 Kenobi

26

Quando si crea la tabella di join, prestare particolare attenzione al requisito che le due tabelle devono essere elencate in ordine alfabetico nel nome / classe di migrazione. Questo può facilmente morderti se i nomi dei tuoi modelli sono simili, ad esempio "abc" e "abb". Se dovessi scappare

rails g migration create_abc_abb_table

Le tue relazioni non funzioneranno come previsto. Devi usare

rails g migration create_abb_abc_table

anziché.


1
Quale viene prima? foo o foo_bar?
B Seven

1
Ho eseguito in una console rails: ["foo_bar", "foo", "foo bar"]. Sort # => ["foo", "foo bar", "foo_bar"] L'ordinamento Unix si presenta allo stesso modo.
Shadoath

6

Per le relazioni HABTM, è necessario creare una tabella di join. C'è solo una tabella di join e quella tabella non dovrebbe avere una colonna id. Prova questa migrazione.

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

È necessario controllare questi tutorial di guida alle guide di relazione


Non penso che tu abbia bisogno di un modello e non vedo nulla nel collegamento sulla necessità di un modello per una relazione HABTM.
B Seven

1
Per velocizzare le query generate, aggiungi indici ai campi id.
Martin Röbert
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.